Sto assumendo per lo scopo di questo esercizio che non vuoi alcuna ripetizione. Ad esempio, le combinazioni per [1..4] sono
[[1],[1,2],[1,2,3],[1,2,3,4],[1,2,4],[1,3],[1,3,4],
[1,4],[2],[2,3],[2,3,4],[2,4],[3],[3,4],[4]]
In caso contrario, il processo è lo stesso per tutte le funzioni ricorsive: individua i casi di base e individua il tuo problema più grande in termini di un problema più piccolo.
Creeremo una funzione subarrays
che prende una lista e restituisce una lista di liste, una per ogni combinazione, quindi:
subarrays :: [a] -> [[a]]
Il caso base è di solito molto semplice. In questo caso, i sottoarray per una lista vuota sono una lista vuota, quindi:
subarrays [] = []
Il passaggio ricorsivo per le funzioni di lista lo spezza quasi sempre nel primo elemento, chiamato "testa" (indicato qui da x
), e il resto dell'elenco, chiamato "coda" (indicato qui da xs
). Presumiamo di conoscere già la risposta corretta per la coda, e la usiamo per costruire una risposta per la testa. Qui assegniamo il simbolo combos
alla risposta per la coda:
subarrays (x:xs) = let combos = subarrays xs
Finora questo è un codice relativamente standard che si trova in quasi tutte le funzioni ricorsive in una forma o nell'altra. La parte difficile ora è che l'abbiamo risolto per una lista più piccola, come aggiungeremo un altro livello?
Per un esempio, supponiamo che combos
contenga tutte le soluzioni per [2,3,4] e stai creando un elenco che contiene anche soluzioni contenenti 1
. In questo caso, puoi fare una delle tre cose per costruire la lista più grande.
- Basta avere
1
di per sé. ( [x]
)
- Basta avere tutte le altre combo da sole. (
combos
)
- hai aggiunto tutte le altre combo con
1
. ( map (x:) combos
)
Quindi restituisci una lista contenente tutte queste possibilità:
[x] : map (x:) combos ++ combos
Questo è davvero tutto quello che c'è da fare. Ecco il programma completo in Haskell con la funzione sum test:
subarrays :: [a] -> [[a]]
subarrays [] = []
subarrays (x:xs) = let combos = subarrays xs
in [x] : map (x:) combos ++ combos
combosSumTo :: Int -> [Int] -> [[Int]]
combosSumTo x = filter (\y -> sum y == x) . subarrays
Per quanto riguarda il modo in cui sono venuto fuori con le tre cose, devi solo esercitarti, testare e ripetere. Quando l'ho scritto per la prima volta, ho dimenticato per sbaglio la parte [x]
, ma questo è apparso nei miei test. Il trucco è non cercare di ricacciare la pila nella tua testa. Supponiamo che la chiamata ricorsiva ti dia la risposta corretta per la coda della lista.