Подтвердить что ты не робот

Использование Y Combinator в С#

Я пытаюсь понять, как писать рекурсивные функции (например, факториал, хотя мои функции намного сложнее) в одной строке. Чтобы сделать это, я подумал об использовании Lambda Calculus Y combinator.

Здесь первое определение:

Y = λf.(λx.f(x x))(λx.f(x x))

Здесь приведенное определение:

Y g = g(Y g)

Я попытался записать их на С# следующим образом:

// 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>. Сначала я попытался набрать его с помощью using, но который не работал, поэтому теперь он определен с помощью delegate dynamic Lambda(dynamic arg);)

Моя факториал лямбда выглядит так (адаптировано из здесь):

Lambda factorial = f => new Lambda(n =>  n == 1 ? 1 : n * f(n - 1));

И я называю это следующим образом:

int result = (int)(Y(factorial))(5);

Однако в обоих случаях (исходные и приведенные формы Y combinator) я получаю исключение. Из того, что я могу предположить из использования приведенной формы, кажется, что она просто заканчивается вызовом Y(factorial(Y(factorial(Y(factorial(... и никогда не заканчивается тем, что фактически вводит факториал лямбда.

Так как у меня нет большого опыта отладки выражений С# лямбда, и я, конечно, не очень разбираюсь в лямбда-исчислении, я действительно не знаю, что происходит или как это исправить.

В случае, если это имеет значение, этот вопрос был вдохновлен попыткой написать однострочный ответ на этот вопрос на С#.

Мое решение следующее:

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 combinator в С#? Если да, то что я делаю неправильно в реализации?
  • Если в С# возможно Y combinator, как бы я сделал решение проблемы с подстроками (функция AllSubstrings) одним слоем?
  • Можно ли использовать Y combinator в С#, есть ли какие-либо другие методы программирования, которые позволяли бы однострочный AllSubstrings?
4b9b3361

Ответ 1

Здесь реализация Y-combinator, которую я использую в С#:

public delegate T S<T>(S<T> s);

public static T U<T>(S<T> s)
{
    return s(s);
}

public static Func<A, Z> Y<A, Z>(Func<Func<A, Z>, Func<A, Z>> f)
{
    return U<Func<A, Z>>(r => a => f(U(r))(a));
}

Я могу использовать его как:

var fact = Y<int, int>(_ => x => x == 0 ? 1 : x * _(x - 1));
var fibo = Y<int, int>(_ => x => x <= 1 ? 1 : _(x - 1) + _(x - 2));

Это действительно пугает меня, поэтому я оставлю следующие две части вашего вопроса вам, учитывая это как отправную точку.


У меня была проблема с функцией.

Вот он:

var allsubstrings =
    Y<string, IEnumerable<string>>
        (_ => x => x.Length == 1
            ? new [] { x }
            : Enumerable
                .Range(0, x.Length)
                .SelectMany(i =>
                    _(x.Remove(i, 1))
                    .SelectMany(z => new [] { x.Substring(i, 1) + z, z }))
                .Distinct());

Конечно, вы запускаете его следующим образом:

allsubstrings("abcd");

Из чего я получил этот результат:

abcd 
bcd 
acd 
cd 
abd 
bd 
ad 
d 
abdc 
bdc 
adc 
dc 
abc 
bc 
ac 
c 
acbd 
cbd 
acdb 
cdb 
adb 
db 
acb 
cb 
ab 
b 
adbc 
dbc 
adcb 
dcb 
bacd 
bad 
badc 
bac 
bcad 
cad 
bcda 
cda 
bda 
da 
bca 
ca 
ba 
a 
bdac 
dac 
bdca 
dca 
cabd 
cadb 
cab 
cbad 
cbda 
cba 
cdab 
dab 
cdba 
dba 
dabc 
dacb 
dbac 
dbca 
dcab 
dcba 

Кажется, что ваш код не Y-Combinator в вашем вопросе пропустил кучу перестановок.