Почему выходной цикл 3, цикл 2, цикл 1, цикл 1? - PullRequest
0 голосов
/ 05 января 2019

Для следующего кода я ожидал, что консоль выведет

Loop 3
Loop 2
Loop 1

Однако вместо

Loop 3
Loop 2
Loop 1
Loop 1 

отображается. Я понимаю, что это рекурсивный метод, то есть сам вызов. Просто изо всех сил пытаюсь понять, как это работает. Это экзаменационный вопрос, с которым я боролся.

Я попытался отладить, и я вижу следующее:

  1. После того, как я перешел к 0, он выходит из цикла while, как и ожидалось
  2. работает обратный леммингов - 1; утверждение, в какой момент я бы подумал, что Леммингс будет равен -1, но вместо этого он возвращается к строке i = WriteToConsole (i - 1); который выполнен
  3. Каким-то образом Лемминги = 1 и я = -1
  4. Это продолжается, но я потерян в этот момент
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace WhileLoopExample
{
    class Program
    {
        private static int WriteToConsole(int Lemmings)
        {
            int i = Lemmings;
            while (i > 0)
            {
                Console.WriteLine("loop {0}", i);
                i = WriteToConsole(i - 1);
            }
            return Lemmings - 1;
        }

        static void Main(string[] args)
        {
            WriteToConsole(3);
            Console.ReadKey();
        }
    }
}

Ответы [ 4 ]

0 голосов
/ 05 января 2019

Ваша проблема в том, что вы не понимаете понятия функция активации . Каждая активация функции имеет свою собственную копию i и собственную копию Lemmings, и они полностью независимы от любой другой активации функции.

Что вас здесь смущает, так это то, что у вас есть пять - да пять - разные вызовы на WriteToConsole, и у всех них есть собственная копия i и их собственная копия lemmings. Давайте проиллюстрируем это, дав инструменту вашей программе показ каждой активации каждой функции и значения каждой переменной в каждой точке:

public class Program
{
    private static int callCount = 0;
    private static int WriteToConsole(int lemmings)
    {
        callCount += 1;
        int currentCall = callCount;

        Console.WriteLine("Call number {0} has Lemmings = {1}", currentCall, lemmings);
        int i = lemmings;
        Console.WriteLine("Call number {0} has i = {1}", currentCall, i);
        while (i > 0)
        {
            Console.WriteLine("Call number {0} in the loop top with i = {1}", currentCall, i);
            i = WriteToConsole(i - 1);
            Console.WriteLine("Call number {0} in the loop bottom with i = {1}", currentCall, i);
        }
        Console.WriteLine("Call number {0} is about to return {1}", currentCall, lemmings - 1);
        return lemmings - 1;
    }

    public static void Main(string[] args)
    {
        WriteToConsole(3);
    }
}

Теперь мы видим, что результат отражает то, что происходит:

Call number 1 has Lemmings = 3
Call number 1 has i = 3
Call number 1 in the loop top with i = 3
Call number 2 has Lemmings = 2
Call number 2 has i = 2
Call number 2 in the loop top with i = 2
Call number 3 has Lemmings = 1
Call number 3 has i = 1
Call number 3 in the loop top with i = 1
Call number 4 has Lemmings = 0
Call number 4 has i = 0
Call number 4 is about to return -1
Call number 3 in the loop bottom with i = -1
Call number 3 is about to return 0
Call number 2 in the loop bottom with i = 0
Call number 2 is about to return 1
Call number 1 in the loop bottom with i = 1
Call number 1 in the loop top with i = 1
Call number 5 has Lemmings = 0
Call number 5 has i = 0
Call number 5 is about to return -1
Call number 1 in the loop bottom with i = -1
Call number 1 is about to return 2

Очень внимательно читайте след , пока не поймете, что происходит. Вы получаете четыре выхода loop x, потому что номер вызова 1 находится в верхней части цикла, когда i равен 3, а после рекурсивного вызова i равен 1. Условие цикла выполнено, поэтому вызовите 1 печатает как loop 3, так и loop 1.

0 голосов
/ 05 января 2019

Чтобы достичь того, что вы хотите, в этом случае вам нужно один цикл или другой механизм итерации , либо с помощью рекурсии (без while), либо с помощью while (но потом без рекурсии).

Вот решение для while без рекурсии:

private static void WriteToConsole(int Lemmings)
{
    int i = Lemmings;
    while (i > 0)
    {
        Console.WriteLine("loop {0}", i);
        i = i - 1; // Decrease i, no need for recursion here
    }
}

Рабочая DotNetFiddle: https://dotnetfiddle.net/kR4th0

0 голосов
/ 05 января 2019

В вашем рекурсивном вызове вы передаете i - 1. Это становится Lemmings - который вы возвращаете как Lemmings - 1.

Есть два -1 события - это то, что вы хотите?

Но если вы посмотрите на то, что происходит:

  • Первый вызов имеет Lemmings как 3 и i как 3.

    Loop 3 выписано.

    Затем происходит рекурсия, переходящая в 3-1:

    • Второй вызов имеет Lemmings как 2 и i как 2.

      Loop 2 выписано.

      Затем происходит рекурсия, переходящая в 2-1:

      • Третий вызов имеет Lemmings как 1 и i как 1.

        Loop 1 выписано.

        Затем происходит рекурсия, переходящая в 1-1:

        • Четвертый вызов имеет Lemmings как 0 и i как 0.

          while не введено, поэтому 0-1 возвращается.

      • Обратно внутрь третьего звонка:

        i назначается -1, поэтому время заканчивается.

        Третий вызов возвращает 1-1.

    • Возвращение во второй звонок:

      i назначается 0, поэтому время заканчивается.

      Второй вызов возвращает 2-1.

  • Обратно внутрь первого звонка:

    i назначается 1, поэтому время продолжается.

    Loop 1 выписано.

Отсюда продолжается выполнение, но все без вывода. Выше, почему вы получаете два Loop 1 с.

0 голосов
/ 05 января 2019

Это потому, что у вас есть цикл и у вас есть рекурсия. Вы должны избавиться от вызова на WriteToConsole(i) внутри WriteToConsole или избавиться от цикла и придерживаться рекурсии.

Кроме того, вы используете возвращаемое значение рекурсивного вызова для влияния на i и, следовательно, на цикл while. Я думаю, что это вызывает основное замешательство.

К счастью, это всего лишь пара шагов, поэтому его можно легко разобрать. Это то, что происходит. Цикл while подразумевается, я просто описываю вывод, модификации i и рекурсивные вызовы.

You call WriteToConsole(3); // Let's call this 'instance' of the function call 'A'
  A prints "Loop 3"
  A calls WriteToConsole(2); // Let's call this 'B'
    B prints "Loop 2"
    B calls WriteToConsole(1) // Let's call this 'C'
      C prints "Loop 1"
      C calls WriteToConsole(0); // Let's call this 'D'
        D doesn't enter it's loop. 
        D returns -1 (Lemmings - 1, where Lemmings is 0), i becomes -1 in C
      C's loop ends because i is -1
      C returns 0, i becomes 0 in B
    B's loop ends, because i is 0
    B returns 1, i becomes 1 in A
  A is still in the loop, since its 'i' > 0, so..
  A prints "Loop 1" (again)
  A calls WriteToConsole(0); // Let's call this 'E'
    E returns -1, i becomes -1 in A
  A returns 2, this value is ignored in your main function.
End of program
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...