Questo è un buon esercizio per diventare più fluente in quel linguaggio di programmazione che intendevi imparare, ma hai solo armeggiato con leggerezza. Ciò implica lavorare con oggetti, usare o simulare chiusure e allungare il sistema di tipi.
Il tuo compito è scrivere codice per gestire liste pigre, quindi utilizzarlo per implementare questo algoritmo per generare numeri di Fibonacci:
Gli esempi di codice sono in Haskell
let fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
in take 40 fibs
Risultato:
[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368,75025,121393,196418,317811,514229,832040,1346269,2178309,3524578,5702887,9227465,14930352,24157817,39088169,63245986]
L'implementazione dell'elenco pigro dovrebbe soddisfare queste linee guida:
- Un nodo Elenco è una delle tre cose:
- Zero: elenco vuoto.
[]
- Contro - Un singolo oggetto, associato a un elenco degli elementi rimanenti:
1 : [2,3,4,5]
(:
è l'operatore contro in Haskell) - Thunk - Un calcolo differito che produce un nodo Elenco quando necessario.
- Zero: elenco vuoto.
- Supporta le seguenti operazioni:
- zero: crea un elenco vuoto.
- contro - Costruisci una cella contro.
- thunk - Costruisci un Thunk, data una funzione che non accetta argomenti e restituisce uno zero o un contro.
- force - Dato un nodo Elenco:
- Se è uno zero o contro, semplicemente restituirlo.
- Se è un Thunk, chiama la sua funzione per ottenere uno zero o contro. Sostituisci il thunk con quel Nil o Contro e restituiscilo.
Nota: la sostituzione del thunk con il suo valore forzato è una parte importante della definizione di "pigro" . Se questo passaggio viene saltato, l'algoritmo di Fibonacci sopra sarà troppo lento.
- vuoto - Verifica se un nodo Elenco è zero (dopo averlo forzato).
- head (alias "car"): ottieni il primo elemento di un elenco (o lancia un attacco se è zero).
- tail (aka "cdr") - Ottieni gli elementi dopo la testa di una lista (o lancia un adattamento se è zero).
- zipWith - Data una funzione binaria (es.
(+)
) e due (possibilmente infiniti) elenchi, applica la funzione agli elementi corrispondenti degli elenchi. Esempio:
zipWith (+) [1,2,3] [1,1,10] == [2,3,13]
- take - Dato un numero N e un elenco (possibilmente infinito), prendi i primi N elementi dell'elenco.
- stampa - Stampa tutti gli elementi in un elenco. Questo dovrebbe funzionare in modo incrementale quando viene fornito un elenco lungo o infinito.
fibs
si usa nella sua stessa definizione. Impostare una ricorsione pigra è un po 'complicato; dovrai fare qualcosa del genere:- Allocare un thunk per
fibs
. Lascialo in uno stato fittizio per ora. - Definire la funzione thunk, che dipende da un riferimento a
fibs
. - Aggiorna il thunk con la sua funzione.
È possibile nascondere questo impianto idraulico definendo una funzione
fix
che chiama una funzione di ritorno in elenco con il proprio valore di ritorno. Considera di fare un breve pisolino in modo che questa idea possa entrare.- Allocare un thunk per
Il polimorfismo (la capacità di lavorare con elenchi di qualsiasi tipo di oggetto) non è richiesto, ma vedi se riesci a trovare un modo per farlo che sia idiomatico nella tua lingua.
- Non preoccuparti della gestione della memoria. Anche le lingue con garbage collection hanno la tendenza a portare in giro oggetti che non userete mai più (ad es. Nello stack di chiamate), quindi non stupitevi se il vostro programma perde memoria mentre attraversa un elenco infinito.
Sentiti libero di deviare leggermente da queste linee guida per soddisfare i dettagli della tua lingua o per esplorare approcci alternativi alle liste pigre.
Regole:
- Scegli una lingua che non conosci bene. Non posso "richiedere" questo, quindi il tag "sistema d'onore". Tuttavia, gli elettori possono controllare la tua cronologia per vedere in quali lingue hai pubblicato.
Non utilizzare il supporto dell'elenco pigro incorporato nella tua lingua per fare tutto. Pubblica qualcosa di sostanziale o almeno interessante.
Haskell è praticamente fuori. Cioè, a meno che tu non faccia qualcosa del genere:
data List a = IORef (ListNode a) data ListNode a = Nil | Cons !a !(List a) | Thunk !(IO (ListNode a))
Nota: la valutazione non rigorosa di Haskell non è off-limits, ma l'implementazione dell'elenco pigro non dovrebbe derivare la sua capacità direttamente da lì. In effetti, sarebbe interessante vedere una soluzione efficiente, puramente funzionale che non richiede pigrizia.
Pitone:
- Non usare itertools.
- I generatori vanno bene, ma li usi, dovrai trovare un modo per memorizzare valori forzati.
zipWith (+) [1,2,3,4,5] [0,0,0] == [1,2,3]
. Tuttavia, questo non ha importanza per l'algoritmo di Fibonacci sopra, poiché entrambi gli argomenti di zipWith sono liste infinite.
fibs
correttamente, poiché dipende da se stessa. Ho aggiornato la domanda per elaborare la ricorsione pigra. FUZxxl l'ha capito da solo.
zipWith
due elenchi di diverse lunghezze?