La parte intricata è il ciclo. Cominciamo da quello. Un ciclo viene in genere convertito in stile funzionale esprimendo l'iterazione con una singola funzione. Un'iterazione è una trasformazione della variabile loop.
Ecco un'implementazione funzionale di un ciclo generale:
loop : v -> (v -> v) -> (v -> Bool) -> v
loop init iter cond_to_cont =
if cond_to_cont init
then loop (iter init) iter cond
else init
Prende (un valore iniziale della variabile loop, la funzione che esprime una singola iterazione [sulla variabile loop]) (una condizione per continuare il ciclo).
Il tuo esempio usa un ciclo su un array, che si interrompe anche. Questa capacità nella tua lingua imperativa è inserita nella lingua stessa. Nella programmazione funzionale tale capacità è di solito implementata a livello di biblioteca. Ecco una possibile implementazione
module Array (foldlc) where
foldlc : v -> (v -> e -> v) -> (v -> Bool) -> Array e -> v
foldlc init iter cond_to_cont arr =
loop
(init, 0)
(λ (val, next_pos) -> (iter val (at next_pos arr), next_pos + 1))
(λ (val, next_pos) -> and (cond_to_cont val) (next_pos < size arr))
Dentro :
Uso una coppia ((val, next_pos)) che contiene la variabile loop visibile all'esterno e la posizione nell'array, che questa funzione nasconde.
La funzione di iterazione è leggermente più complessa rispetto al ciclo generale, questa versione consente di utilizzare l'elemento corrente dell'array. [È in forma al curry .]
Tali funzioni sono generalmente denominate "fold".
Metto una "l" nel nome per indicare che l'accumulo degli elementi dell'array è fatto in modo associativo sinistro; imitare l'abitudine dei linguaggi di programmazione imperativa di iterare un array dall'indice basso a quello alto.
Ho messo una "c" nel nome per indicare che questa versione di fold ha una condizione che controlla se e quando il ciclo deve essere fermato in anticipo.
Naturalmente è probabile che tali funzioni di utilità siano prontamente disponibili nella libreria di base fornita con il linguaggio di programmazione funzionale utilizzato. Li ho scritti qui per la dimostrazione.
Ora che abbiamo tutti gli strumenti che sono nella lingua nel caso imperativo, possiamo rivolgerci per implementare la funzionalità specifica del tuo esempio.
La variabile nel tuo ciclo è una coppia ('risposta', un valore booleano che codifica se continuare).
iter : (Int, Bool) -> Int -> (Int, Bool)
iter (answer, cont) collection_element =
let new_answer = answer + collection_element
in case new_answer of
10 -> (new_answer, false)
150 -> (new_answer + 100, true)
_ -> (new_answer, true)
Nota che ho usato una nuova "variabile" "new_answer". Questo perché nella programmazione funzionale non posso cambiare il valore di una "variabile" già inizializzata. Non mi preoccupo delle prestazioni, il compilatore potrebbe riutilizzare la memoria di "risposta" per "new_answer" tramite l'analisi del tempo di vita, se ritiene che sia più efficiente.
Incorporando questo nella nostra funzione loop sviluppata in precedenza:
doSomeCalc :: Array Int -> Int
doSomeCalc arr = fst (Array.foldlc (0, true) iter snd arr)
"Matrice" qui è il nome del modulo che è la funzione di esportazione foldlc.
"pugno", "secondo" indica funzioni che restituiscono il primo, secondo componente del suo parametro di coppia
fst : (x, y) -> x
snd : (x, y) -> y
In questo caso lo stile "senza punti" aumenta la leggibilità dell'implementazione di doSomeCalc:
doSomeCalc = Array.foldlc (0, true) iter snd >>> fst
(>>>) è la composizione della funzione: (>>>) : (a -> b) -> (b -> c) -> (a -> c)
È lo stesso di sopra, solo il parametro "arr" è escluso da entrambi i lati dell'equazione di definizione.
Un'ultima cosa: verificare il caso (array == null). In linguaggi di programmazione meglio progettati, ma anche in linguaggi mal progettati con una certa disciplina di base si usa piuttosto un tipo opzionale per esprimere la non esistenza. Questo non ha molto a che fare con la programmazione funzionale, di cui alla fine si tratta la domanda, quindi non me ne occupo.
break
ereturn answer
può essere sostituita da unreturn
ciclo interno. In FP potresti implementare questo ritorno anticipato usando le continuazioni, vedi ad esempio en.wikipedia.org/wiki/Continuation