Implementazione della mappa con ricorsione della coda

0

Sto cercando di risolvere questo esercizio . Si tratta di reimplementare la funzione map in haskell per scopi di apprendimento. Ho trovato una soluzione che non sfoglia tutti gli elementi della lista (semplice elenco collegato, quindi l'accesso all'ultimo elemento sfoglia tutti gli elenchi) ad ogni iterazione, ma non ne ho trovato uno che sia ricorsivo in coda.

accumulateRec :: (a -> b) -> [a] -> [b]
accumulateRec func [] = []
accumulateRec func (h:t) = (func h) : accumulateRec func t

Esiste un modo per implementare la mappa in modo ricorsivo in coda e senza sfogliare l'elenco a ogni iterazione?

PS: exercism.io è un modo fantastico per imparare una nuova lingua.

    
posta Moebius 25.06.2016 - 23:57
fonte

2 risposte

1

La ricorsione della coda non è una buona idea in Haskell con le funzioni di lista, perché la ricorsione in coda impedisce alla valutazione lenta di restituire un risultato parziale.

Comunque, per rispondere alla tua domanda, è possibile scrivere una funzione "mappa invertita" (come map eccetto che l'ordine degli elementi è invertito) che è ricorsivo in coda e non passa attraverso l'elenco di ogni passaggio. Mantiene un accumulatore che è l'elenco dei risultati finora (indietro) e per ogni nuovo elemento nell'input antepone il risultato all'accumulatore (ed è per questo che è indietro).

reverseMap :: (a -> b) -> [a] -> [b]
reverseMap func = helper [] where
  helper acc [] = acc
  helper acc (h:t) = helper (func h : acc) t

Naturalmente, dato che hai ottenuto i risultati all'indietro, devi invertirlo di nuovo, e dal momento che reverse è anch'esso ricorsivo in coda, l'intera operazione è ricorsiva in coda.

myMap :: (a -> b) -> [a] -> [b]
myMap func = helper [] where
  helper acc [] = reverse acc
  helper acc (h:t) = helper (func h : acc) t
    
risposta data 28.06.2016 - 21:42
fonte
2

Le funzioni ricorsive in Haskell sono attività complicate.

Haskell è pigro, quindi nella maggior parte delle situazioni questa funzione sarà valutata solo se necessario, e ciò significa che la prima cosa valutata sarà sempre il tipo costruttore : , poiché tutto ciò che usa il risultato deve prima guardare al costruttore del tipo prima che possa guardare qualsiasi valore all'interno.

Quindi Haskell prima crea un nodo lista contenente uno stub non valutato per il valore func h e uno stub non valutato per la coda accumulateRec func t . E solo quando questi valori sono effettivamente necessari valuterà questi stub.

O in altre parole, in Haskell, qualsiasi funzione ricorsiva che utilizza il risultato di ricorsione come argomento per un costruttore di dati probabilmente non è effettivamente ricorsiva nel modello di esecuzione.

La linea di fondo è che la funzione che hai già ha il vantaggio che le funzioni ricorsive della coda hanno, vale a dire la valutazione dello spazio costante invece di costruire una grande pila di ricorsioni.

    
risposta data 26.06.2016 - 00:12
fonte

Leggi altre domande sui tag