La récursivité est naturel, quand vous travaillez sur des niveaux plus élevés de l'abstraction. La programmation fonctionnelle n'est pas seulement au sujet de l'encodage de fonctions; il est à propos d'exploitation à des niveaux plus élevés de l'abstraction, où vous naturellement utiliser des fonctions. Utilisation de fonctions, il est naturel de réutiliser les même fonction (appel à nouveau), à partir de quel que soit le contexte où il fait sens.
Le monde est construit par la répétition de semblables ou de même des blocs de construction. Si vous coupez un morceau de tissu en deux, vous avez deux morceaux de tissu. Induction mathématique est au cœur des mathématiques. Nous, les humains, count ( 1,2,3...). De toute façon inductive défini chose (comme, {les nombres au-dessus de 1} sont {2, et les numéros ci-dessus 2}) est naturel à gérer par une fonction récursive, selon le même cas, par laquelle la chose est définie/construit.
La récursivité est partout. Toute boucle itérative est une récursivité dans le déguisement de toute façon, parce que quand vous entrez cette boucle, vous entrez à nouveau la même boucle à nouveau (juste avec peut-être des différentes variables de boucle). Il n'est donc pas comme inventer de nouveaux concepts à l'informatique, c'est plus de découvrir les fondations, et de la rendre explicite.
Ainsi, la récursivité est naturel. Nous venons d'écrire quelques lois à propos de notre problème, certaines équations impliquant la fonction nous sommes à la définition qui permettent de préserver certains invariants (sous l'hypothèse que la fonction est cohérente définie), re-précisant le problème en termes simplifiés, et le tour est joué! Nous avons la solution.
Un exemple, une fonction pour calculer la longueur de la liste (une inductif défini récursif de type de données). Supposons qu'il est défini, et renvoie la liste de la longueur, de la non-surprise. Quelles sont les lois qu'il doit obéir? Ce que l'invariant est préservé dans ce simplification d'un problème?
Le plus immédiat est de prendre la liste, en dehors de son élément de tête, et le reste - un.k.un. la liste de la queue (en fonction de la façon dont une liste est définie/construit). La loi est,
length (x:xs) = 1 + length (xs)
D'uh! Mais que dire de la liste vide? Il faut que
length [] = 0
Donc comment peut-on écrire une telle fonction?... Attendez... Nous l'avons écrit déjà! (En Haskell, si vous vous demandez).
Tous nous avons besoin d'une langue pour permettre un tel style de programmation, c'est qu'il a coût total de possession (et peut-être, un peu luxueusement, TRMCO), donc il n'y a pas de pile de blow-up, et nous sommes ensemble.
Une autre chose est la pureté, l'immuabilité des variables de code et/ou de structure de données (dossiers champs, etc).
Ce qui fait, en plus de libérer nos esprits d'avoir à suivre ce qui change quand il prend le temps explicitement apparente dans notre code, au lieu de se cacher dans notre "évolution" mutable variables/données. On ne peut "changer" dans le code impératif la valeur d'une variable à partir de maintenant - nous ne pouvons pas changer sa valeur dans le passé, peut-on?
Et on se retrouve donc avec des listes de changement enregistré de l'histoire, avec une manière explicite le changement apparent dans le code: au lieu de x := x + 2
nous écrire let x2 = x1 + 2
. Il fait de raisonner sur le code beaucoup plus facile.
À l'adresse de l'immuabilité dans le contexte de la queue de la récursivité avec TCO, considérer cette queue récursive ré-écrire la fonction ci-dessus length
en vertu de l'accumulateur argument de paradigme:
length xs = length2 0 xs -- the invariant:
length2 a [] = a -- 1st arg plus
length2 a (x:xs) = length2 (a+1) xs -- length of 2nd arg
Ici, coût total de possession des moyens d'appel cadre de la réutilisation, en plus du saut direct, et donc, l'appel de la chaîne pour length [1,2,3]
peut être considéré comme réellement la mutation du cadre de pile d'appel entrées correspondant à la fonction de paramètres:
length [1,2,3]
length2 0 [1,2,3] -- a=0 (x:xs)=[1,2,3]
length2 1 [2,3] -- a=1 (x:xs)=[2,3]
length2 2 [3] -- a=2 (x:xs)=[3]
length2 3 [] -- a=3 (x:xs)=[]
3
Dans une langue pure, sans aucune valeur-la mutation de primitives, la seule façon d'exprimer le changement est de transmettre les valeurs mises à jour en tant qu'arguments à une fonction, à être transformées. Si la poursuite du traitement est le même qu'avant, que nous avons bien sûr d'appeler la même fonction pour que, en passant de la mise à jour des valeurs comme arguments. Et c'est la récursivité.
Et la suite rend l'ensemble de l'histoire de calcul de la longueur d'une liste d'arguments explicites (et disponible pour la réutilisation, si nécessaire):
length xs = last results
where
results = length3 0 xs
length3 a [] = [a]
length3 a (x:xs) = a : length3 (a+1) xs
En Haskell c'est variablement connu comme gardé la récursivité, ou corecursion (au moins je pense que c'est).