Как не генерировать переполнение стека, когда подпроцедура вызывает себя?

Этот код генерирует переполнение стека. Я знаю, что это вызвано вызовом самой процедуры. Что я могу сделать, чтобы избежать переполнения стека? Вызов подпроцедуры и генерация нового случайного числа — это самое простое, что можно сделать, однако это приводит к переполнению. Случайно сгенерированное число выбирает случайный предмет инвентаря, затем оператор if сопоставляет это число (случайный предмет инвентаря) с количеством этого предмета из инвентаря колоды, чтобы убедиться, что оно не меньше 1. Если инвентарь этого предмета 0, else воспроизводит и перезапускает процедуру, генерируя новое случайное число и повторяя процесс заново. В другой процедуре у меня есть функция, согласно которой, если инвентарь колоды становится полностью пустым, то стопка сброса пополняет колоду, делая стопку сброса пустой, поэтому никогда не должно быть случая, когда все случайно сгенерированные числа могут быть связаны с элементом инвентаря. 0.

Интересно, можно ли как-то заставить генератор случайных чисел

Number = (DeckGroup(Rnd.Next(0, DeckGroup.Count)).ID)

не генерировать числа для элементов инвентаря DeckGroup(Number).QuantityInteger, которые равны нулю. При этом мне даже не нужно было бы вспоминать функцию. Случайное число генерируется другой ветвью в той же структурной группе.

Private Sub PlayElse()
        Dim CardCheckBoxArray() As CheckBox = {CardCheckBox1, CardCheckBox2, CardCheckBox3, CardCheckBox4, CardCheckBox5}
        'Reset Number Generator
            Number = (DeckGroup(Rnd.Next(0, DeckGroup.Count)).ID)
            Dim PlayerQuantitySubtractionInteger As Integer
            For PlayerQuantitySubtractionInteger = ChecksDynamicA To ChecksDynamicB
                If CardCheckBoxArray(TextBoxInteger).Checked = True And DeckGroup(Number).QuantityInteger > 0 Then
                    DeckGroup(Number).QuantityInteger -= 1
                    'Select the Player depending value of T
                    Select Case T
                        Case 0
                            Player1HandGroup(Number).QuantityInteger += 1
                        Case 1
                            Player1HandGroup(Number).QuantityInteger2 += 1
                        Case 2
                            Player1HandGroup(Number).QuantityInteger3 += 1
                        Case 3
                            Player1HandGroup(Number).QuantityInteger4 += 1
                        Case 4
                            Player1HandGroup(Number).QuantityInteger5 += 1
                    End Select
                    CardTypeArray(PlayerQuantitySubtractionInteger) = Player1HandGroup(Number).CardType
                    CardCheckBoxArray(TextBoxInteger).Text = Player1HandGroup(Number).CardNameString
                    NumberArray(PlayerQuantitySubtractionInteger) = Number
            Else
                If CardCheckBoxArray(TextBoxInteger).Checked = True And DeckGroup(Number).QuantityInteger < 0 Then
                    Call PlayElse()
                End If
                End If
        Next PlayerQuantitySubtractionInteger
    End Sub

person Gakko no Ato    schedule 26.03.2013    source источник
comment
попробуйте изменить Number = (DeckGroup(Rnd.Next(0, DeckGroup.Count)).ID) на Number = (DeckGroup(Rnd.Next(1, DeckGroup.Count)).ID), что приведет к тому, что 1 будет наименьшим случайным числом, которое может быть возвращено.   -  person Mike_OBrien    schedule 26.03.2013
comment
Однако мне нужно сгенерировать 0 для индекса 0.   -  person Gakko no Ato    schedule 26.03.2013
comment
о, я вижу, я неправильно прочитал. поэтому вам нужно отсеять все идентификаторы объектов DeckGroup с QuantityInteger, равным 0?   -  person Mike_OBrien    schedule 26.03.2013
comment
точно, при этом не будет выбрана ни одна карта с целым числом 0.   -  person Gakko no Ato    schedule 26.03.2013


Ответы (2)


Вы можете использовать LINQ, чтобы отсеять все объекты, которые вы никогда не хотите получать в первую очередь, а затем использовать коллекцию, возвращенную linq, вместо исходной коллекции.

Что-то типа:

Private Sub PlayElse()
    Dim CardCheckBoxArray() As CheckBox = {CardCheckBox1, CardCheckBox2, CardCheckBox3, CardCheckBox4, CardCheckBox5}
    'Reset Number Generator
    Dim temp As IEnumerable(Of LunchMoneyGame.LunchMoneyMainForm.Group) = From r In DeckGroup Where r.QuantityInteger > 0 Select r
    If temp IsNot Nothing AndAlso temp.Any Then
        Number = (temp(Rnd.Next(0, temp.Count)).ID)

        ' ** Edit **: This will ensure that you only got 1 object back from the LINQ which can tell you whether or not you have bad data. You *can* exclude this check but its good practice to include it.

        Dim obj As LunchMoneyGame.LunchMoneyMainForm.Group = Nothing

        Dim t = From r In temp Where r.ID = Number Select r
        If t IsNot Nothing AndAlso t.Count = 1 Then
            obj = t(0)
        End If

        If obj IsNot Nothing Then
            Dim PlayerQuantitySubtractionInteger As Integer
            For PlayerQuantitySubtractionInteger = ChecksDynamicA To ChecksDynamicB
               ' ** Edit **
               obj.QuantityInteger -= 1
               'Select the Player depending value of T
                Select Case T
                    Case 0
                        Player1HandGroup(Number).QuantityInteger += 1
                    Case 1
                        Player1HandGroup(Number).QuantityInteger2 += 1
                    Case 2
                        Player1HandGroup(Number).QuantityInteger3 += 1
                    Case 3
                        Player1HandGroup(Number).QuantityInteger4 += 1
                    Case 4
                        Player1HandGroup(Number).QuantityInteger5 += 1
                End Select
                CardTypeArray(PlayerQuantitySubtractionInteger) = Player1HandGroup(Number).CardType
                CardCheckBoxArray(TextBoxInteger).Text = Player1HandGroup(Number).CardNameString
                NumberArray(PlayerQuantitySubtractionInteger) = Number
            Next PlayerQuantitySubtractionInteger
        End If
    End If
End Sub
person Mike_OBrien    schedule 26.03.2013
comment
temp(Number).QuantityInteger -= 1 сообщение об ошибке: выражение является значением и поэтому не может быть целью назначения. - person Gakko no Ato; 26.03.2013
comment
@Gakko no Ato: отредактировал код. До того, как мой код пытался использовать идентификатор объекта в качестве индекса. Я добавил туда второй оператор Linq, чтобы получить фактический объект, с которым вы хотите работать. Это может вызвать неявные ошибки/предупреждения преобразования в зависимости от того, как настроена ваша среда и какие параметры у вас есть. вы можете обойти это, явно diming obj как любой класс, который хранится в коллекции DeckGroup. - person Mike_OBrien; 26.03.2013
comment
«QuantityInteger» не является членом «System.Collections.Generic.IEnumerable(Of LunchMoneyGame.LunchMoneyMainForm.Group)». Я даже пытался сделать obj новой группой (изменив obj.QuantityInteger -= 1 на `obj(0).QuantityInteger -= 1` в моей группе структур, однако затем я снова получаю ошибку выражения. - person Gakko no Ato; 26.03.2013
comment
Попробуйте явно ввести свою коллекцию и объект obj, как я сделал в последних изменениях (вам может потребоваться ctype r в linq, но ваша среда должна сказать вам, какой тип он ожидает. - person Mike_OBrien; 26.03.2013
comment
Хорошо, где бы я Ctype его. Я признаю, что я немного любитель, поэтому этот проект — мой способ улучшить себя. Набрав именно так, как вы это сделали, я получаю ошибку: Value of type 'System.Collections.Generic.IEnumerable(Of LunchMoneyGame.LunchMoneyMainForm.Group)' cannot be converted to 'LunchMoneyGame.LunchMoneyMainForm.Group' . Подчеркнутая строка была Dim obj As LunchMoneyGame.LunchMoneyMainForm.Group = From r In temp Where r.ID = Number Select r справа от оператора. - person Gakko no Ato; 27.03.2013
comment
Моя ошибка, я забыл изменить LINQ. Ошибка говорит о том, что он возвращает коллекцию, когда ожидает один объект. Итак, попробуйте Dim obj As LunchMoneyGame.LunchMoneyMainForm.Group = (From r In temp Where r.ID = Number Select r).FirstOrDefault. Я внесу несколько изменений в ответ и покажу вам другой способ сделать это, который даст вам больше контроля над проверкой того, что вы действительно получаете то, что ожидаете от LINQ. - person Mike_OBrien; 27.03.2013

Пройдитесь по списку и определите только те, которые действительны. Затем случайным образом вытяните из этого набора. Вот его простая версия. Вы также можете использовать LINQ, но это должно быть достаточно ясно:

Dim validDeckGroupsIndexes As New List(Of Integer)

For ndx As Integer = 0 to DeckGroup.Count - 1
   If DeckGroup(ndx).QuantityInteger > 0 Then
      validDeckGroupsIndexes .Add(ndx)
   End If
Next ndx

Затем используйте это:

Dim deckGroupNdx As Integer = Rnd.Next(0, validDeckGroupsIndexes.Count)

Number = DeckGroup(deckGroupNdx).ID
person tcarvin    schedule 26.03.2013
comment
Работает, однако я получаю ошибку индекса вне диапазона. Цифры от 0 до 13, но максимум 14. Я предполагаю, что он подсчитал, сколько индексов было в целом. Возможно, Number = DeckGroup(deckGroupNdx - 1).ID сработает? - person Gakko no Ato; 26.03.2013