return
выходит только из этого конкретного вызова метода. Это не прерывает все времена, когда sequenceCreator()
был вызван.
В этом случае это должно быть в порядке, потому что он возвращается к этой строке:
return sequenceCreator(nextVal, numOfSteps);
, который, в свою очередь, снова вернется к своему вызывающему абоненту и т. Д., Пока вы окончательно не решите все рекурсивные вызовы.
Но я мог бы написать такой метод вместо этого:
public int sequenceCreator(int currentVal)
{
if (currentVal == 1) return 1;
if (currentVal % 2 == 0)
{
return 1 + sequenceCreator(currentVal / 2);
}
else // (currentVal % 2 > 0)
{
return 1 + sequenceCreator(currentVal * 3 + 1);
}
}
Этот код дает тот же результат для того же ввода, но с меньшим количеством кода, который легче понять, и без необходимости передавать дополнительное состояние между вызовами методов.
Ради интереса, мы можем еще больше сократить код с помощью троичного оператора (но я не рекомендую использовать эту версию, поскольку ухудшается читаемость imo):
public int sequenceCreator(int currentVal)
{
if (currentVal == 1) return 1;
return 1 + sequenceCreatetor(currentValue % 2 == 0? currentVal / 2 : currentVal * 3 + 1);
}
Я показываю эту версию для иллюстрации , почему используется рекурсия. В основе их лежат рекурсивные проблемы. Для каждого рекурсивного решения существует соответствующее решение, которое опирается только на традиционные методы, используя вместо этого стек (рекурсивное решение просто «скрывает» стек, полагаясь на стек вызовов программы). Однако для некоторых типов проблем рекурсия позволяет радикально уменьшить количество кода, необходимого для ее решения. И поэтому у нас есть и обратное; если вы обнаружите, что используете стек, может быть способом использовать рекурсию, чтобы значительно упростить проблему. В этом случае тело метода состоит всего из двух строк, и, если бы я действительно этого хотел, я мог бы поместить его в одну строку кода.
Вы также должны понимать, что не создает последовательность. Он создает ровно одно значение в тех случаях, когда он вообще сходится. Если вы действительно хотите создать последовательность, вам нужно вернуть IEnumerable
, в идеале используя ключевое слово yield
:
public IEnumerable<int> sequenceCreator(int currentVal)
{
if (currentVal == 1) yield return 1;
if (currentVal % 2 == 0)
{
yield return 1 + sequenceCreator(currentVal / 2);
}
else // (currentVal % 2 > 0)
{
yield return 1 + sequenceCreator(currentVal * 3 + 1);
}
}
По логике вещей, я думаю, вы здесь в безопасности, и он сойдет. «Финальный» или базовый вариант этого метода - это ввод 1
. Вызовы, где ввод даже уменьшается к этому базовому случаю. Вызовы, где ввод нечетный, увеличиваются по сравнению с базовым регистром, но таким образом, когда следующий ввод всегда четный и всегда отличается четным значением, чем мы пытались ранее. В конечном итоге мы надеемся получить в два или три раза больше, чем два, которые останутся даже при уменьшении вплоть до 3
или 2
, а затем 1
и затем выйдут.
Тем не менее, я обеспокоен тем, что может существовать некоторые значения, которые никогда не достигают этого состояния, или переполняют целое число до того, как они могут, или после увеличения в несколько раз уменьшают обратно до четного значения, которое мы уже пытались , создавая тем самым бесконечный цикл.