Я пытаюсь понять, как писать рекурсивные функции (например, факториал, хотя мои функции намного сложнее) в одной строке. Для этого я подумал об использовании Lambda Calculus'Y combinator.Использование Y Combinator в C#
Вот первое определение:
Y = λf.(λx.f(x x))(λx.f(x x))
Вот приведенное определение:
Y g = g(Y g)
Я попытался записать их в C# как это:
// Original
Lambda Y = f => (new Lambda(x => f(x(x)))(new Lambda(x => f(x(x)))));
// Reduced
Lambda Y = null; Y = g => g(Y(g));
(Lambda
является Func<dynamic, dynamic>
. Сначала я попытался набрать его., но that didn't work, так что теперь он определен с delegate dynamic Lambda(dynamic arg);
)
Мой факторный лямбда выглядит следующим образом (адаптировано из here):
Lambda factorial = f => new Lambda(n => n == 1 ? 1 : n * f(n - 1));
И я называю это так:
int result = (int)(Y(factorial))(5);
Однако , в обоих случаях (оригинальные и уменьшенные формы Y-комбинатора), я получаю исключение переполнения стека. Из того, что я могу предположить, используя приведенную форму, кажется, что она просто заканчивается вызовом Y(factorial(Y(factorial(Y(factorial(...
и никогда не заканчивается фактически , вводя факториал лямбда.
Так как у меня нет большого опыта отладки C# лямбда-выражений, а я , конечно, не понимают большую часть лямбда-исчисления, я действительно не знаю, что происходит или как это исправить.
В случае, если это имеет значение, этот вопрос был вдохновлен попыткой написать однострочный ответ на this question в C#.
Мое решение заключается в следующем:
static IEnumerable<string> AllSubstrings(string input)
{
return (from i in Enumerable.Range(0, input.Length)
from j in Enumerable.Range(1, input.Length - i)
select input.Substring(i, j))
.SelectMany(substr => getPermutations(substr, substr.Length));
}
static IEnumerable<string> getPermutations(string input, int length)
{
return length == 1 ? input.Select(ch => ch.ToString()) :
getPermutations(input, length - 1).SelectMany(
perm => input.Where(elem => !perm.Contains(elem)),
(str1, str2) => str1 + str2);
}
// Call like this:
string[] result = AllSubstrings("abcd").ToArray();
Моя цель состоит в том, чтобы написать getPermutations
как один линии само-рекурсивное лямбда, так что я могу вставить его в SelectMany
в AllSubstrings
и сделать одну гильзу из от AllSubstrings
.
Мои вопросы следующие:
- ли Y возможно комбинатор в C#? Если да, то что я делаю неправильно в реализации?
- Если комбинатор Y является возможным в C#, как бы я сделал свое решение проблемы с подстроками (функция
AllSubstrings
) одним слоем? - Независимо от того, является ли комбинатор Y , а не Возможны ли в C#, есть ли какие-либо другие методы программирования, которые позволяли бы однострочный
AllSubstrings
?
'Y g = g (Y g)' только хорошо с ленивой оценкой. С ожидаемой оценкой обходным путем является использование eta-расширения: 'Y g = g (\ x-> (Y g) x)'. Или, может быть, 'Y g x = g (\ x-> (Y g) x) x' будет проще. –
@WillNess Спасибо! Это сработало, когда я написал его как «Lambda y = null; y = g => g (новый Lambda (x => (y (g)) (x))): «Ну, я думаю, это отвечает на первый вопрос. – Jashaszun
поможет ли вам, если бы я дал вам полу-один лайнер Haskell? это 'concatMap перестановки. последовательностей с 'последовательностями (x: xs) = [a | b <-sequences xs, a <- [x: b, b]]; sequence [] = [[]] 'и' permutations' стандартная функция. –