#vb.net #recursion
#vb.net #рекурсия
Вопрос:
Сейчас я немного застрял, пытаясь определить наилучшее решение для предотвращения бесконечного цикла рекурсии. Возможно, это не точно «рекурсия», но это набор вызовов функций, которые, я могу в значительной степени гарантировать, будут вызывать друг друга бесконечно, если я не смогу найти решение.
Пытаясь выяснить, как объяснить проблему, кажется, что лучший способ, который я могу придумать, — начать с некоторого упрощенного (и отредактированного) кода. Для этого примера я буду использовать класс и ученика.
Public Class Classroom
Public Property ClassroomID As Integer
Public Property ClassroomDescription As String
Public Property Students As List(Of Student)
Public Sub New(ByVal ClassroomID As Integer)
Initialize()
GetClassroomDetail(ClassroomID)
End Sub
Public Sub GetClassroomDetail(ByVal ClassroomID As Integer)
Dim Reader As SqlDataReader
' HERE'S WHERE I MAKE THE DATABASE CALL TO
' GET THE CLASSROOM RECORD DETAILS
FillClassroomRecord(Reader)
End Sub
Private Sub FillClassroomRecord(Reader)
While Reader.Read
ClassroomID = CType(Reader("ClassroomID"), Integer)
ClassroomDescription = CType(Reader("ClassroomDescription"), String)
Students = GetClassroomStudents(ClassroomID)
End While
End Sub
Private Function GetClassroomStudents(ByVal ClassroomID As Integer) As List(Of Student)
Dim StudentData As DataTable
Dim ClassroomStudents As New List(Of Student)
' I PULL A LIST OF STUDENTS RELATED TO THE SPECIFIC CLASSROOMID
For Each StudentRow As DataRow In StudentData.Rows
Dim NewStudent As New Student(CType(StudentRow("studentid"), Integer))
ClassroomStudents.Add(NewStudent)
Next StudentRow
Return ClassroomStudents
End Function
End Class
Пока все довольно просто. Однако проблема заключается в том, что один и тот же учащийся может быть привязан к нескольким классам. Я хочу иметь аналогичный метод в Student
объекте, чтобы иметь возможность извлекать все связанные классы для этого учащегося.
Public Class Student
Public Property StudentID As Integer
Public Property Name As String
Public Property Classrooms As List(Of Classroom)
...
Private Function GetStudentClassrooms(ByVal StudentID As Integer) As List(Of Classroom)
Dim ClassroomData As DataTable
Dim StudentClassrooms As New List(Of Classroom)
' PULL A LIST OF CLASSROOMS RELATED TO THE SPECIFIC STUDENTID
For Each ClassroomRow As DataRow In ClassroomData.Rows
Dim NewClassroom As New Classroom(CType(ClassroomRow("classroomid"), Integer))
StudentClassrooms.Add(NewClassroom)
Next ClassroomRow
Return StudentClassrooms
End Function
End Class
Итак, мое оцепенение на данный момент заключается в том, как мне предотвратить постоянное переключение между классами и студентами, заполняющими одни и те же объекты снова и снова в бесконечном цикле?
Единственное, что я могу придумать, это установить где-нибудь логическую переменную, которую я могу установить, чтобы определить, следует ли продолжать детализацию. Проблема в том, что мой мозг на данный момент немного перегрет, и я не могу понять, как реализовать такое решение.
В некоторых результатах поиска, которые я выполнил, также упоминалась возможность какого-то «обратного отслеживания», что звучит круто, но, похоже, не очень похоже на работу в ситуации такого типа. Конечно, я могу ошибаться в этом, и я хотел бы увидеть какую-либо реализацию, которая каким-то образом способна разумно определять, просто ли я повторяю одни и те же вещи снова и снова.
Что я хотел бы видеть, так это то, что Classroom
создание объекта верхнего уровня должно забрать свои Student
объекты, которые должны забрать любые дополнительные Classroom
объекты, с которыми они связаны, включая каждый из объектов этого Classroom
объекта Student
, а затем это останавливается.
Я надеюсь, что все имеет смысл. На самом деле это даже сложнее, чем это, поскольку существуют другие подобные объекты, которые будут привязаны обратно к объекту верхнего уровня ( Classroom
), который также должен следовать тем же основным правилам — не копаться слишком глубоко в «вспомогательных» записях и предотвращать бесконечный рекурсивный цикл
Если необходимо какое-либо разъяснение, пожалуйста, дайте мне знать. Я искренне ценю любую помощь, которую вы можете предоставить.
Комментарии:
1. Почему у
ClassRoom
должны быть какие-либо интересы к другимClassRoom
объектам?2. Если я понимаю ваш вопрос, ответ «нет». Не в том смысле, что одному
Classroom
нужно знать, что другойClassroom
существует сам по себе . Только в том, что касаетсяStudent
.3. Ваши классы немного перегружены. Каждому классу не нужен список учащихся — достаточно списка идентификаторов, представляющих учащихся.
GetClassroomStudents
создает НОВЫХ УЧЕНИКОВ вместо получения списка текущих учеников из некоторой их коллекции. Если бы вам пришлось повторять свой список, чтобы отметить некоторые отсутствующие или установленные оценки, это было бы сделано для другого набора учащихся, чем в основной коллекции. В противном случае linq упрощает извлечение наборов на основе того или иного свойства.4. Я думаю, вы можете неправильно понять.
GetClassroomStudents
На самом деле не создается новых учеников, это просто создание нового экземпляраStudent
объекта для каждого из существующихStudents
в этомClassroom
. База данных настроена с таблицей «многие ко многим» «index» между двумя таблицами, которые содержат эти элементы данных, которые я использую для получения определенныхClassroom
/Student
пар. Если бы я пришел к этому, используяStudent
в качестве «верхнего уровня», я бы хотел извлечь все,Classrooms
которымStudent
присвоен, наряду с любым другимStudents
в этомClassroom
…5. Является ли это многопользовательским приложением, в котором учащиеся и классы могут обновляться в любое время, что требует нового вызова базы данных для каждого запроса о студентах и классах, или вы могли бы хранить все данные на стороне клиента во время выполнения?
Ответ №1:
Я думаю, у меня может быть идея, как это решить, но я хотел бы получить некоторые отзывы о моей идее.
Если я создам перегрузку New
конструктора, который принимает логическое значение ( GetRelatedDetails
), то я могу вызвать его изначально с установленным флагом True
. Практически все остальное должно оставаться неизменным, кроме передачи этого значения по цепочке. Используя приведенный выше пример, это выглядело бы примерно так:
Public Class Classroom
Public Property ClassroomID As Integer
Public Property ClassroomDescription As String
Public Property Students As List(Of Student)
Public Sub New(ByVal ClassroomID As Integer)
Initialize()
GetClassroomDetail(ClassroomID, False)
End Sub
' OVERLOAD WITH BOOLEAN VALUE TO GET RELATED DETAILS
Public Sub New(ByVal ClassroomID As Integer, ByVal GetRelatedDetails As Boolean)
Initialize()
GetClassroomDetail(ClassroomID, GetRelatedDetails)
End Sub
Public Sub GetClassroomDetail(ByVal ClassroomID As Integer, ByVal GetRelatedDetails As Boolean)
Dim Reader As SqlDataReader
' HERE'S WHERE I MAKE THE CALL TO GET THE
' CLASSROOM RECORD DETAILS FROM THE DATABASE
FillClassroomRecord(Reader, GetRelatedDetails)
End Sub
Private Sub FillClassroomRecord(ByVal Reader As SqlDataReader, ByVal GetRelatedDetails As Boolean)
While Reader.Read
ClassroomID = CType(Reader("ClassroomID"), Integer)
ClassroomDescription = CType(Reader("ClassroomDescription"), String)
If GetRelatedDetails Then
Students = GetClassroomStudents(ClassroomID)
End If
End While
End Sub
Private Function GetClassroomStudents(ByVal ClassroomID As Integer) As List(Of Student)
Dim StudentData As DataTable
Dim ClassroomStudents As New List(Of Student)
' I PULL A LIST OF STUDENTS RELATED TO THE SPECIFIC CLASSROOMID
For Each StudentRow As DataRow In StudentData.Rows
Dim NewStudent As New Student(CType(StudentRow("studentid"), Integer))
ClassroomStudents.Add(NewStudent)
Next StudentRow
Return ClassroomStudents
End Function
End Class
Затем Student
объект делает в основном то же самое:
Public Class Student
Public Property StudentID As Integer
Public Property StudentName As String
Public Property Classrooms As List(Of Classroom)
Public Sub New(ByVal StudentID As Integer)
Initialize()
GetStudentDetail(StudentID, False)
End Sub
' OVERLOAD WITH BOOLEAN VALUE TO GET RELATED DETAILS
Public Sub New(ByVal StudentID As Integer, ByVal GetRelatedDetails As Boolean)
Initialize()
GetStudentDetail(StudentID, GetRelatedDetails)
End Sub
Public Sub GetStudentDetail(ByVal StudentID As Integer, ByVal GetRelatedDetails As Boolean)
Dim Reader As SqlDataReader
' HERE'S WHERE I MAKE THE CALL TO GET THE
' STUDENT RECORD DETAILS FROM THE DATABASE
FillStudentRecord(Reader, GetRelatedDetails)
End Sub
Private Sub FillStudentRecord(ByVal Reader As SqlDataReader, ByVal GetRelatedDetails As Boolean)
While Reader.Read
StudentID = CType(Reader("StudentID"), Integer)
StudentName = CType(Reader("StudentName"), String)
If GetRelatedDetails Then
Classrooms = GetStudentClassrooms(StudentID)
End If
End While
End Sub
Private Function GetStudentClassrooms(ByVal StudentID As Integer) As List(Of Classroom)
Dim ClassroomData As DataTable
Dim StudentClassrooms As New List(Of Classroom)
' PULL A LIST OF CLASSROOMS RELATED TO THE SPECIFIC STUDENTID
For Each ClassroomRow As DataRow In ClassroomData.Rows
Dim NewClassroom As New Classroom(CType(ClassroomRow("classroomid"), Integer))
StudentClassrooms.Add(NewClassroom)
Next ClassroomRow
Return StudentClassrooms
End Function
End Class
Вы заметите, что, даже если я передам значение True
исходному объекту, когда он перейдет к Fill
методу и перейдет к GetClassroomStudents
или GetStudentClassrooms
, это просто вызовет New
перегрузку конструктора, значение которой по умолчанию равно False
. Таким образом, я предполагаю, что это должно предотвратить бесконечное повторение одних и тех же записей снова и снова.
Конечно, я открыт для любых других предложений или идей о наилучшем способе реализации этого, но я думаю, что это то, что я собираюсь попытаться сделать сейчас.