Sto cercando di capire come scrivere funzioni ricorsive (ad esempio fattoriale, anche se le mie funzioni sono molto più complicato) in una sola riga. Per fare questo, ho pensato di usare la Lambda Calculus'Y combinator.Utilizzando la Y Combinator in C#
Ecco la prima definizione:
Y = λf.(λx.f(x x))(λx.f(x x))
Ecco la definizione ridotta:
Y g = g(Y g)
ho cercato di scrivere in C# come questo:
// 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
è un Func<dynamic, dynamic>
Per prima cosa ho provato a digitarlo con using
, ma that didn't work, così ora è definito con delegate dynamic Lambda(dynamic arg);
)
mio lambda fattoriale simile a questa (adattato da here):
Lambda factorial = f => new Lambda(n => n == 1 ? 1 : n * f(n - 1));
E io lo chiamo così:
int result = (int)(Y(factorial))(5);
Tuttavia , in entrambi i casi (forme originali e ridotte del combinatore Y), alla fine con un'eccezione di overflow dello stack. Da quello che posso supporre di utilizzare la forma ridotta, sembra come se finisce solo per chiamare Y(factorial(Y(factorial(Y(factorial(...
e mai finisce in realtà entrare lambda fattoriale.
Poiché non ho molta esperienza nel debug di espressioni C# lambda e I certamente non capisco molto del calcolo lambda, non so davvero cosa sta succedendo o come risolverlo.
Nel caso in cui è importante, questa domanda è stato ispirato, cercando di dare una risposta a una linea per this question in C#.
La mia soluzione è la seguente:
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();
Il mio obiettivo è quello di scrivere getPermutations
come lambda auto-ricorsiva di una riga in modo che possa inserirla nella SelectMany
in AllSubstrings
e fare una battuta fuori di AllSubstrings
.
Le mie domande sono le seguenti:
- È il combinatore Y possibile in C#? Se sì, cosa sto sbagliando nell'implementazione?
- Se il combinatore Y è possibile in C#, come potrei rendere la soluzione al problema delle sottostringhe (la funzione
AllSubstrings
) a un liner? - Se il combinatore Y è non possibile in C#, ci sono altri metodi di programmazione che consentirebbero il rivestimento singolo
AllSubstrings
?
'Y g = g (Y g)' va bene solo con la valutazione lazy. Con la gradita valutazione, la soluzione è usare l'espansione eta: 'Y g = g (\ x-> (Y g) x)'. O forse 'Y g x = g (\ x-> (Y g) x) x' sarà più facile. –
@WillNess Grazie! Questo ha funzionato quando l'ho scritto come 'Lambda y = null; y = g => g (nuovo Lambda (x => (y (g)) (x))); 'Suppongo che risponda alla prima domanda. – Jashaszun
ti aiuterà se ti ho dato un semi-one-liner Haskell? è 'permatations concatMap. sequenze' con 'sequenze (x: xs) = [a | b <-sequences xs, a <- [x: b, b]]; sequenze [] = [[]] 'e' permutazioni' una funzione standard. –