errore iniziale: Ricorsione infinita
Stepping attraverso il codice:
sumint :: Int -> Int
Ehi, un tipo di firma. Sei forte.
sumint x
| x==0 = 0
Un caso base, fresco.
| x==1 = 1
Un caso totalmente inutile. OK, certo ... tranne. 1 non è nemmeno così perché lo includiamo nella somma? Dovrebbe essere zero (o rimosso completamente).
| (x `mod` 2 == 0) && (x >= 2) = x + (sumint x-2)
La carne del problema è qui. 1. X è pari, ottimo. 2. X è positivo, sì. Il risultato è x + (sumint x) - 2
No!
- Errore 1: l'applicazione della funzione di avviso si associa più agli operatori, pertanto dovrebbe essere
x + sumint (x-2)
.
Questo è il motivo per il sovraccarico dello stack. sumint 2 == 2 + (sumint 2) - 2 + (sumint 2) -2 + (sumint 2) -2 + ...
, yay ricorsione infinita.
| (x `mod` 2 /= 0) && (x >= 1) = x + (sumint x-1)
Un altro caso ... quote ... positivo ... ma perché stiamo aggiungendo in x? Vuoi aggiungere uniformi, non quote. Quindi fissare il problema di cui sopra, allo stesso tempo otteniamo:
- Errore 2: Se si determina
x
è dispari, non aggiungere in x
. Basta usare sumint (x-1)
.
Quindi sei fuori dai casi. Cosa succede se x non è positivo? Hai bisogno di (altro) caso.
| otherwise = 0
prossimo numero: No Capitalizzazione
Il problema ora è che si sta costruendo una grande thunk (calcolo non valutata) invece di operare nello spazio costante accumulando il risultato, come si procede. Notate se espandiamo il vostro calcolo per, diciamo, 6 otteniamo:
sumint 6 = 6 + sumint (6-2)
= 6 + 4 + sumint (4-2)
= 6 + 4 + 2 + sumint (2-2)
= 6 + 4 + 2 + 0
davvero non si desidera mantenere tutte quelle aggiunte separano, sarebbe bene passare in un accumulatore come ad esempio:
sumint x = go x 0
where
go n accumulator
| n <= 0 = accumulator
| odd n = go (n-1) accumulator
| otherwise = go (n-2) (accumulator + n)
Nota a margine: altri cittadini stackoverflow potrebbero menzionare la necessità di rendere l'accumulatore rigido, che è una buona forma. Non voglio distrarre l'attuale richiedente con quella discussione qui. Si noti che l'utilizzo dell'ottimizzazione, -O2
, è sufficiente.
Solutions idiomatiche
Tutte le soluzioni di cui sopra sono piuttosto prolisso. L'operazione generale, che scorre su un elenco con una funzione e un accumulatore, è un tipo di fold
. I fold sono uno dei tanti traversamenti della struttura altamente ottimizzati comuni nella programmazione funzionale. In questo caso una "piega a sinistra rigorosa" è il tipico candidato (da Data.List
). Questo è foldl'
'il primo ('
) per convenzione significa che è rigoroso mentre il l
significa sinistra.
sumint n = foldl' (+) 0 [val | val <- [0..n], even val]
Qui abbiamo passato la lista per ottenere la nostra somma. Per creare l'elenco di interesse, abbiamo utilizzato la comprensione delle liste, innanzitutto elencando i valori da 0..n
e ignorando qualsiasi valore che non soddisfa il predicato even
.
Possiamo ulteriormente pulire questo e migliorarla utilizzando la funzione e la lista sum
di comprensione che passi a 2, dandoci così solo le dispari che desiderate:
sumint n = sum [0,2..n]
Grande, grazie. Esattamente quello di cui avevo bisogno. –
Dettaglio minuscolo: 'go' dovrebbe essere sostituito con' (+) 'nella definizione di' sumint' che usa 'foldl''. Farei questa modifica ma il cambiamento è inferiore a 6 caratteri. – liminalisht