La partie complexe est la boucle. Commençons par cela. Une boucle est généralement convertie en style fonctionnel en exprimant l'itération avec une seule fonction. Une itération est une transformation de la variable de boucle.
Voici une implémentation fonctionnelle d'une boucle générale:
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
Cela prend (une valeur initiale de la variable de boucle, la fonction qui exprime une seule itération [sur la variable de boucle]) (une condition pour continuer la boucle).
Votre exemple utilise une boucle sur un tableau, qui se casse également. Cette capacité dans votre langue impérative est incorporée dans la langue elle-même. En programmation fonctionnelle, une telle capacité est généralement implémentée au niveau de la bibliothèque. Voici une implémentation possible
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))
En cela:
J'utilise une paire ((val, next_pos)) qui contient la variable de boucle visible à l'extérieur et la position dans le tableau, que cette fonction cache.
La fonction d'itération est légèrement plus complexe que dans la boucle générale, cette version permet d'utiliser l'élément courant du tableau. [Il est sous forme de curry .]
De telles fonctions sont généralement appelées "fold".
Je mets un "l" dans le nom pour indiquer que l'accumulation des éléments du tableau se fait de manière associative à gauche; imiter l'habitude des langages de programmation impératifs pour itérer un tableau d'index faible à élevé.
Je mets un "c" dans le nom pour indiquer que cette version de fold prend une condition qui contrôle si et quand la boucle doit être arrêtée plus tôt.
Bien entendu, ces fonctions utilitaires sont susceptibles d'être facilement disponibles dans la bibliothèque de base livrée avec le langage de programmation fonctionnel utilisé. Je leur ai écrit ici pour démonstration.
Maintenant que nous avons tous les outils qui sont dans la langue dans le cas impératif, nous pouvons à présent mettre en œuvre les fonctionnalités spécifiques de votre exemple.
La variable dans votre boucle est une paire ('answer', un booléen qui indique s'il faut continuer).
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)
Notez que j'ai utilisé une nouvelle "variable" 'new_answer'. En effet, en programmation fonctionnelle, je ne peux pas changer la valeur d'une "variable" déjà initialisée. Je ne m'inquiète pas des performances, le compilateur pourrait réutiliser la mémoire de 'answer' pour 'new_answer' via une analyse à vie, s'il pense que cela est plus efficace.
En incorporant cela dans notre fonction de boucle développée précédemment:
doSomeCalc :: Array Int -> Int
doSomeCalc arr = fst (Array.foldlc (0, true) iter snd arr)
"Array" est le nom du module qui exporte la fonction foldlc.
"poing", "deuxième" représente les fonctions qui retournent la première, deuxième composante de son paramètre de paire
fst : (x, y) -> x
snd : (x, y) -> y
Dans ce cas, le style "sans point" augmente la lisibilité de l'implémentation de doSomeCalc:
doSomeCalc = Array.foldlc (0, true) iter snd >>> fst
(>>>) est la composition de la fonction: (>>>) : (a -> b) -> (b -> c) -> (a -> c)
C'est la même chose que ci-dessus, juste le paramètre "arr" est omis des deux côtés de l'équation qui définit.
Une dernière chose: vérifier la casse (array == null). Dans des langages de programmation mieux conçus, mais même dans des langages mal conçus avec une discipline de base, on utilise plutôt un type optionnel pour exprimer la non-existence. Cela n’a pas grand-chose à voir avec la programmation fonctionnelle, qui est au coeur de la question, donc je ne la traite pas.
break
etreturn answer
peut être remplacée par unereturn
boucle interne. Dans FP, vous pouvez implémenter ce retour rapide en utilisant des continuations, voir par exemple en.wikipedia.org/wiki/Continuation