2014-12-19 12 views
10

stavo sperimentando con Haskell, e durante il tentativo di migliorare la leggibilità del mio codice ho improvvisamente cambiato il comportamento di esso. Avrei pensato che queste due varianti sarebbero equivalenti.Perché queste funzioni in modo diverso valutati

originale:

f :: Eq c => c -> c -> [[c]] -> [[c]] 
f d c acc 
    | c == d = [] : acc 
    | otherwise = ([c] ++ (head acc)) : tail acc 

split :: Eq a => a -> [a] -> [[a]] 
split delim = foldr (f delim) [[]] 

Ecco il secondo:

f' :: Eq c => c -> c -> [[c]] -> [[c]] 
f' d c (currentWord:wordsSoFar) 
    | c == d = [] : currentWord : wordsSoFar 
    | otherwise = (c : currentWord) : wordsSoFar 

split' :: Eq a => a -> [a] -> [[a]] 
split' delim = foldr (f' delim) [[]] 

Ecco i risultati dell'esecuzione del due:

*Main> take 1 (split 5 [1..]) 
[[1,2,3,4]] 
*Main> take 1 (split' 5 [1..]) 
*** Exception: stack overflow 
+0

nel tuo secondo codice prova a cambiare 'split'' a' split' – maioman

+0

@maioman Che non risponde a nulla. – Jubobs

+0

'foldr' è un tipo di cosa" magica "che genera una ricorsione molto profonda, ma poi viene ottimizzata (o meno) dal compilatore. Ti suggerirei di provare a riscrivere il tuo codice in termini di 'foldl'', che funziona a profondità di ricorsione lineare. La grande differenza in due esempi è che il primo 'f' non esamina è' acc' su richiesta. –

risposta

8

tua prima versione ha bisogno solo per valutare acc quando chiami head e tail, quindi nessuna valutazione l'azione ha luogo quando c == d.

La seconda versione ha bisogno di sapere se acc è vuota o non prima che lo faccia qualsiasi altra cosa come nessuno degli altri codice deve eseguire se il pattern non corrisponde. Ciò significa che acc deve essere valutato anche se c == d. Questo porta ad un ciclo infinito.

È possibile effettuare la seconda opera versione utilizzando un modello inconfutabile come questo:

f' d c ~(currentWord:wordsSoFar) = 

Rendendo il modello inconfutabile, stai dicendo che si sa che il modello corrisponderà, in modo che nessun controllo è necessaria . Se acc erano vuoti, ciò causava un errore quando (e se) si utilizzava currentWord e wordsSoFar invece di un errore di modello non esaustivo che si verificava immediatamente (e indipendentemente dal fatto che siano effettivamente utilizzati currentWord e wordsSoFar).

+1

questo non è corretto da un punto di vista: anche quando 'c/= d',' acc' non è ancora forzato se viene richiesta solo la testa del risultato di 'f'. –

+0

@ sepp2k Perché 'acc' deve essere completamente valutato nel secondo caso? Perché non valutarlo solo a WHNF? – Sibi

+0

Supponevo che intendessi 'head' e' tail' invece di 'hd' e' tl', quindi ho modificato la risposta di conseguenza. Sentiti libero di cambiarlo se questo è stato un errore. – Shoe

2

Penso questo dovrebbe essere equivalente:

f d c acc | c == d = [] : acc 
f d c (currentWord:wordsSoFar) = (c : currentWord) : wordsSoFar 

Si noti che, se c == d allora non tentare di stabilire se acc è vuota o non. (Tutte le versioni del tuo codice falliranno effettivamente se c == d e acc == []; presumo che ciò non accada mai.)

+0

Penso che 'acc @ (currentWord: wordsSoFar)' dovrebbe funzionare bene – d12frosted

Problemi correlati