TL;DR;
В С# у вас есть гарантии, что ленивая функция итератора, которая ничего не вызывает, кроме самой себя и не имеет допустимого условия выхода из рекурсии, не приведет к переполнению стека?
Подробный вопрос:
Я знаю, что, как правило, вы не получаете гарантий от команды Tail Call Optimization (TCO), генерируемой компилятором С# (или JIT), поэтому, пока вы можете получить TCO, никаких гарантий нет.
Учитывая это признание TCO, мне интересно, работают ли ленивые функции итератора (используя yield return
и т.д.) из-за их природы в качестве сопрограммы - каждый хвостовой вызов в одном даже занимает пространство стека? Моя интуиция сопрограмм из-за их повторного входа состоит в том, что каждый хвостовой вызов оптимизируется по умолчанию, поскольку способность выпрыгивать из функции и в следующую из родительского кадра вместо создания нового кадра кажется естественным.
Является ли это поведение в С# или рекурсивными вызовами функций С# -тератора С# создает новый кадр из текущего, а не появляется в родительский фрейм и повторно вводится с новыми параметрами?
Пример:
public static IEnumerable<IEnumerable<T>> GeneratePermutations<T>(this IEnumerable<T> choices, int numberToChoose)
{
if (numberToChoose == 1)
{
foreach (var choice in choices)
yield return new T[] { choice };
yield break;
}
var subPermutations = choices.SelectMany(choice =>
choices.Where(elem => !EqualityComparer<T>.Default.Equals(elem, choice))
.GeneratePermutations(numberToChoose - 1)
.Select(permutation => (new T[] { choice }).Concat(permutation)));
foreach (var perm in subPermutations)
yield return perm;
}
Моя интуиция основана на приведенном выше примере subPermutations
- это просто куча вычислений, кажется, что при вызове итерации он может знать это кучевое вычисление (это часть функций sig, что она является функцией итератора), и, следовательно, немедленно выскочить из этого текущего кадра и расширить вычисление кучи в новый фрейм - не обойдя лишнее пространство стека над тем, что было до попытки рекурсивного вызова...
Эта интуиция может быть абсолютно необоснованной...