No, non è il combinatore Y; sei solo a metà strada. Hai ancora bisogno di calcolare l'auto-applicazione all'interno delle funzioni "seme" a cui stai applicando. Cioè, invece di questo:
Func<dynamic, Func<dynamic, dynamic>> fact =
self => n => n == 0 ? 1 : n * self(self)(n - 1);
si dovrebbe avere questo:
Func<dynamic, Func<dynamic, dynamic>> fact =
self => n => n == 0 ? 1 : n * self(n - 1);
Annotare la singola occorrenza di self
nella seconda definizione in contrasto con le due ricorrenze nella prima definizione.
(a cura di aggiungere :) BTW, dal momento che l'utilizzo di C# simula il lambda calcolo con la valutazione delle chiamate per valore, il punto fisso-Combinator che vuoi è the one often called Z, not Y
(a cura di nuovo di elaborare :) Il equazione che descrive Y
è questo (vedi the wikipedia page per la derivazione):
Y g = g (Y g)
Ma nei linguaggi di programmazione più pratici, si valuta l'argomento di una funzione prima di chiamare la funzione. Nella comunità dei linguaggi di programmazione, si chiama valutazione call-by-value (da non confondere con il modo in cui i programmatori C/C++/Fortran/etc distinguono "call by value" vs "call by reference" vs "call by copy-restore" , eccetera).
Ma se lo facessimo, ci saremmo
Y g = g (Y g) = g (g (Y g)) = g (g (g (Y g))) = ...
Cioè, ci piacerebbe trascorrere tutto il nostro tempo costruendo la funzione ricorsiva e mai andare in giro a applicare esso.
Nella valutazione della chiamata per nome, d'altra parte, si applica una funzione, qui g
, all'espressione di argomento non valutato, qui (Y g)
. Ma se g
è come fact
, è in attesa di un altro argomento prima che faccia qualcosa. Quindi dovremmo aspettare il secondo argomento a g
prima di provare a valutare (Y g)
e, a seconda di cosa funzioni la funzione (cioè, se ha un caso base), potremmo non aver bisogno di valutare (Y g)
. Ecco perché lo Y
funziona per la valutazione del nome per nome.
La correzione per la chiamata per valore consiste nel modificare l'equazione.Invece di Y g = g (Y g)
, vogliamo qualcosa di simile al seguente equazione invece:.
Z g = g (λx. (Z g) x)
(Penso che ho avuto l'equazione giusta, o vicino al giusto grado di calcolare fuori e vedere se si adatta con la definizione di Z
.
Un modo per pensare a questo è invece di calcolare "l'intera funzione ricorsiva" e passandogli a g
, gli passiamo una funzione che calcolerà la funzione ricorsiva un po 'alla volta --- e solo quando abbiamo bisogno di un po 'di più per applicarlo a un argomento (x
).
Ahi, il mio cervello. Immagino di aver * chiesto * di farlo ... – Benjol
Ho qualche possibilità di approfondire la tua BTW? È leggermente sopra la mia testa (ma poi la maggior parte di questo è ...) – Benjol
Grazie per l'ulteriore elaborazione - ero confuso dall'interpretazione di call-by-value. Sono appena arrivato al tuo esempio ricorsivo ('Z = f => f (f (f (f)));' funzionerà per il maggior numero di 'f' come includo ...), ora sto lavorando il prossimo passo ... – Benjol