In che modo x giunge a significare fix x nella prima definizione?
fix f = let x = f x in x
Let attacchi a Haskell sono ricorsivi
Prima di tutto, si rende conto che Haskell consente attacchi let ricorsive. Quello che Haskell chiama "let", alcuni altri linguaggi chiamano "letrec". Questo sembra abbastanza normale per le definizioni di funzione. Per esempio:
ghci> let fac n = if n == 0 then 1 else n * fac (n - 1) in fac 5
120
ma può sembrare piuttosto strano per le definizioni di valore. Tuttavia, i valori possono essere definiti ricorsivamente, a causa della non rigore di Haskell.
ghci> take 5 (let ones = 1 : ones in ones)
[1,1,1,1,1]
Vedi A gentle introduction to Haskell sezioni 3.3 e 3.4 per ulteriori elaborazioni su pigrizia di Haskell.
Thunk in GHC
In GHC, un'espressione non ancora non valutata è avvolto in un "thunk": una promessa per eseguire il calcolo. I thunk vengono valutati solo quando assolutamente devono essere. Supponiamo di voler fix someFunction
. Secondo la definizione di fix
, che è
let x = someFunction x in x
Ora, che cosa vede GHC è qualcosa di simile.
let x = MAKE A THUNK in x
Quindi ha felicemente un tonfo per voi e si muove a destra lungo fino a quando l'utente chiede di sapere che cosa in realtà è x
.
valutazione Esempio
Tale espressione del thunk avviene solo per riferirsi a se stesso. Prendiamo l'esempio ones
e lo riscriviamo per usare fix
.
ghci> take 5 (let ones recur = 1 : recur in fix ones)
[1,1,1,1,1]
Quindi che aspetto avrà?
Possiamo incorporare ones
come la funzione anonima \recur -> 1 : recur
per una dimostrazione più chiara.
take 5 (fix (\recur -> 1 : recur))
-- expand definition of fix
take 5 (let x = (\recur -> 1 : recur) x in x)
Ora poi, che èx
? Beh, anche se non siamo del tutto sicuri di cosa x
è, possiamo ancora andare avanti con l'applicazione funzioni:
take 5 (let x = 1 : x in x)
Ehi guarda, siamo tornati alla definizione che avevamo prima.
take 5 (let ones = 1 : ones in ones)
Quindi, se si ritiene di capire come che si lavora, allora avete una buona sensazione di come fix
opere.
C'è qualche vantaggio di utilizzare la prima definizione rispetto al secondo?
Sì. Il problema è che la seconda versione può causare una perdita di spazio , anche con le ottimizzazioni. Vedere GHC trac ticket #5205, per un problema simile con la definizione di forever
. Questo è il motivo per cui ho parlato di thunk: perché let x = f x in x
assegna solo un thunk: il thunk x
.
La prima definizione è più efficiente perché lega un nodo. Ad esempio, confronta 'fix1 (1 :) !! 1000000' e 'fix2 (1 :) !! 1000000'. – is7s
Che cosa significa "annodare un nodo"? Puoi indicarmi un link? –
@VansonSamuel http://www.haskell.org/haskellwiki/Tying_the_Knot –