Je lis sur la programmation fonctionnelle et j'ai remarqué que la correspondance de modèles est mentionnée dans de nombreux articles comme l'une des fonctionnalités de base des langages fonctionnels.
Quelqu'un peut-il expliquer à un développeur Java / C ++ / JavaScript ce que cela signifie?
Réponses:
Comprendre la correspondance de modèles nécessite d'expliquer trois parties:
Types de données algébriques en un mot
Les langages fonctionnels de type ML vous permettent de définir des types de données simples appelés «unions disjointes» ou «types de données algébriques». Ces structures de données sont de simples conteneurs et peuvent être définies de manière récursive. Par exemple:
définit une structure de données de type pile. Pensez-y comme équivalent à ce C #:
Ainsi, les identificateurs
Cons
etNil
définissent une classe simple, où leof x * y * z * ...
définit un constructeur et certains types de données. Les paramètres du constructeur ne sont pas nommés, ils sont identifiés par position et type de données.Vous créez des instances de votre
a list
classe en tant que telles:Ce qui équivaut à:
Correspondance de motifs en quelques mots
La correspondance de modèles est une sorte de test de type. Supposons donc que nous ayons créé un objet de pile comme celui ci-dessus, nous pouvons implémenter des méthodes pour jeter un coup d'œil et faire apparaître la pile comme suit:
Les méthodes ci-dessus sont équivalentes (bien que non implémentées en tant que telles) au C # suivant:
(Presque toujours, les langages ML implémentent la correspondance de modèles sans tests de type ou casts au moment de l'exécution, donc le code C # est quelque peu trompeur.
Décomposition de la structure des données en quelques mots
Ok, revenons à la méthode peek:
L'astuce consiste à comprendre que les identificateurs
hd
ettl
sont des variables (errm ... puisqu'ils sont immuables, ils ne sont pas vraiment des "variables", mais des "valeurs";)). Sis
a le typeCons
, alors nous allons extraire ses valeurs du constructeur et les lier aux variables nomméeshd
ettl
.La correspondance de motifs est utile car elle nous permet de décomposer une structure de données par sa forme au lieu de son contenu . Alors imaginez si nous définissons un arbre binaire comme suit:
Nous pouvons définir certaines rotations d'arbres comme suit:
(Le
let rotateRight = function
constructeur est le sucre de syntaxe pourlet rotateRight s = match s with ...
.)Ainsi, en plus de lier la structure de données aux variables, nous pouvons également l'explorer. Disons que nous avons un nœud
let x = Node(Nil, 1, Nil)
. Si nous appelonsrotateLeft x
, nous testonsx
le premier modèle, qui ne correspond pas car le bon enfant a le typeNil
au lieu deNode
. Ilx -> x
passera au modèle suivant , qui correspondra à n'importe quelle entrée et le renverra sans modification.À titre de comparaison, nous écririons les méthodes ci-dessus en C # comme suit:
Pour sérieusement.
La correspondance des motifs est géniale
Vous pouvez implémenter quelque chose de similaire à la correspondance de modèles en C # en utilisant le modèle de visiteur , mais ce n'est pas aussi flexible car vous ne pouvez pas décomposer efficacement des structures de données complexes. De plus, si vous utilisez la correspondance de motifs, le compilateur vous dira si vous avez omis un cas . C'est vraiment génial?
Pensez à la façon dont vous implémenteriez des fonctionnalités similaires en C # ou en langages sans correspondance de modèle. Pensez à la façon dont vous le feriez sans tests de test et sans cast au moment de l'exécution. Ce n'est certainement pas difficile , juste encombrant et encombrant. Et vous ne faites pas vérifier par le compilateur pour vous assurer que vous avez couvert tous les cas.
Ainsi, la correspondance de modèles vous aide à décomposer et à parcourir les structures de données dans une syntaxe très pratique et compacte, elle permet au compilateur de vérifier la logique de votre code, au moins un peu. Il vraiment est une caractéristique de tueur.
la source
Réponse courte: L' appariement de motifs survient parce que les langages fonctionnels traitent le signe égal comme une assertion d'équivalence au lieu d'affectation.
Réponse longue: La correspondance de modèles est une forme de répartition basée sur la «forme» de la valeur qui est donnée. Dans un langage fonctionnel, les types de données que vous définissez sont généralement ce que l'on appelle des unions discriminées ou des types de données algébriques. Par exemple, qu'est-ce qu'une liste (liée)? Une liste chaînée
List
d'objets d'un certain typea
est soit la liste vide,Nil
soit un élément de typea
Cons
édité sur aList a
(une liste dea
s). En Haskell (le langage fonctionnel avec lequel je suis le plus familier), nous écrivons ceciToutes les unions discriminées sont définies de cette manière: un seul type a un nombre fixe de manières différentes de le créer; les créateurs, comme
Nil
etCons
ici, sont appelés constructeurs. Cela signifie qu'une valeur de typeList a
aurait pu être créée avec deux constructeurs différents - elle pourrait avoir deux formes différentes. Supposons donc que nous voulions écrire unehead
fonction pour obtenir le premier élément de la liste. Dans Haskell, nous écririons ceci commePuisque les
List a
valeurs peuvent être de deux types différents, nous devons gérer chacune séparément; c'est la correspondance de motif. Danshead x
, six
correspond au modèleNil
, alors nous exécutons le premier cas; s'il correspond au modèleCons h _
, nous exécutons le second.Réponse courte, expliquée: Je pense que l'une des meilleures façons de penser à ce comportement est de changer la façon dont vous pensez du signe égal. Dans les langages entre accolades, en gros,
=
désigne l'affectation:a = b
signifie «fairea
enb
». Dans beaucoup de langages fonctionnels, cependant,=
dénote une affirmation d'égalité:let Cons a (Cons b Nil) = frob x
affirme que la chose à gaucheCons a (Cons b Nil)
,, est équivalente à la chose à droitefrob x
,; de plus, toutes les variables utilisées à gauche deviennent visibles. C'est aussi ce qui se passe avec les arguments de fonction: nous affirmons que le premier argument ressembleNil
, et si ce n'est pas le cas, nous continuons à vérifier.la source
Cons
dire?Cons
est le contre- tructeur qui construit une liste (liée) à partir d'une tête (laa
) et d'une queue (laList a
). Le nom vient de Lisp. Dans Haskell, pour le type de liste intégré, c'est l':
opérateur (qui se prononce toujours "contre").Cela signifie qu'au lieu d'écrire
Tu peux écrire
Hé, C ++ prend également en charge la correspondance de modèles.
la source
La correspondance de modèle est un peu comme des méthodes surchargées sur les stéroïdes. Le cas le plus simple serait à peu près le même que celui que vous avez vu en java, les arguments sont une liste de types avec des noms. La méthode correcte à appeler est basée sur les arguments transmis et elle sert également à affecter ces arguments au nom du paramètre.
Les modèles vont juste un peu plus loin et peuvent déstructurer encore plus les arguments passés. Il peut également potentiellement utiliser des gardes pour correspondre en fonction de la valeur de l'argument. Pour démontrer, je vais faire comme si JavaScript avait une correspondance de modèle.
Dans foo2, il s'attend à ce que a soit un tableau, il sépare le deuxième argument, attend un objet avec deux accessoires (prop1, prop2) et attribue les valeurs de ces propriétés aux variables d et e, puis s'attend à ce que le troisième argument soit 35.
Contrairement à JavaScript, les langages avec correspondance de modèles autorisent généralement plusieurs fonctions avec le même nom, mais des modèles différents. De cette façon, c'est comme une surcharge de méthode. Je vais donner un exemple en erlang:
Brouillez un peu vos yeux et vous pouvez l'imaginer en javascript. Quelque chose comme ça peut-être:
Le fait est que lorsque vous appelez fibo, l'implémentation qu'il utilise est basée sur les arguments, mais où Java est limité aux types comme seul moyen de surcharge, la correspondance de modèles peut faire plus.
Au-delà de la surcharge de fonction comme indiqué ici, le même principe peut être appliqué à d'autres endroits, tels que les déclarations de cas ou les assingments de déstructuration. JavaScript a même cela dans la version 1.7 .
la source
La correspondance de modèles vous permet de faire correspondre une valeur (ou un objet) à certains modèles pour sélectionner une branche du code. Du point de vue C ++, cela peut sembler un peu similaire à l'
switch
instruction. Dans les langages fonctionnels, la correspondance de modèle peut être utilisée pour la correspondance sur des valeurs primitives standard telles que des entiers. Cependant, il est plus utile pour les types composés.Tout d'abord, démontrons la correspondance de modèles sur les valeurs primitives (en utilisant le pseudo-C ++ étendu
switch
):La seconde utilisation concerne les types de données fonctionnels tels que les tuples (qui vous permettent de stocker plusieurs objets dans une seule valeur) et les unions discriminées qui vous permettent de créer un type pouvant contenir l'une des nombreuses options. Cela ressemble un peu,
enum
sauf que chaque étiquette peut également porter certaines valeurs. Dans une syntaxe pseudo-C ++:Une valeur de type
Shape
peut maintenant contenir soitRectangle
avec toutes les coordonnées, soit aCircle
avec le centre et le rayon. La correspondance de motifs vous permet d'écrire une fonction pour travailler avec leShape
type:Enfin, vous pouvez également utiliser des modèles imbriqués qui combinent les deux fonctionnalités. Par exemple, vous pouvez utiliser
Circle(0, 0, radius)
pour faire correspondre toutes les formes qui ont le centre au point [0, 0] et ont un rayon quelconque (la valeur du rayon sera affectée à la nouvelle variableradius
).Cela peut sembler un peu inconnu du point de vue C ++, mais j'espère que mon pseudo-C ++ clarifiera l'explication. La programmation fonctionnelle est basée sur des concepts assez différents, elle a donc plus de sens dans un langage fonctionnel!
la source
La correspondance de modèles est l'endroit où l'interpréteur de votre langue choisira une fonction particulière en fonction de la structure et du contenu des arguments que vous lui donnez.
Ce n'est pas seulement une fonctionnalité de langage fonctionnel, mais il est disponible pour de nombreuses langues différentes.
La première fois que je suis tombé sur cette idée, c'est lorsque j'ai appris le prologue où il est vraiment au cœur du langage.
par exemple
Le code ci-dessus donnera le dernier élément d'une liste. L'entrée arg est le premier et le résultat est le second.
S'il n'y a qu'un seul élément dans la liste, l'interpréteur choisira la première version et le second argument sera mis à égalité au premier, c'est-à-dire qu'une valeur sera attribuée au résultat.
Si la liste a à la fois une tête et une queue, l'interprète choisira la deuxième version et la répétera jusqu'à ce qu'il ne reste qu'un seul élément dans la liste.
la source
Pour de nombreuses personnes, il est plus facile de choisir un nouveau concept si des exemples simples sont fournis, alors allons-y:
Supposons que vous ayez une liste de trois entiers et que vous vouliez ajouter le premier et le troisième élément. Sans correspondance de modèle, vous pouvez le faire comme ceci (exemples dans Haskell):
Maintenant, bien que ce soit un exemple de jouet, imaginez que nous aimerions lier le premier et le troisième entier à des variables et les additionner:
Cette extraction de valeurs à partir d'une structure de données est ce que fait la correspondance de modèles. Vous "miroir" fondamentalement la structure de quelque chose, en donnant des variables à lier pour les lieux d'intérêt:
Lorsque vous appelez cette fonction avec [1,2,3] comme argument, [1,2,3] sera unifié avec [first
_
,, third], liant d'abord à 1, troisième à 3 et rejetant 2 (_
est un espace réservé pour les choses qui ne vous intéressent pas).Maintenant, si vous vouliez seulement faire correspondre les listes avec 2 comme deuxième élément, vous pouvez le faire comme ceci:
Cela ne fonctionnera que pour les listes avec 2 comme deuxième élément et lèvera une exception dans le cas contraire, car aucune définition pour addFirstAndThird n'est donnée pour les listes non correspondantes.
Jusqu'à présent, nous n'utilisions la correspondance de modèles que pour la déstructuration de la liaison. Au-dessus de cela, vous pouvez donner plusieurs définitions de la même fonction, où la première définition correspondante est utilisée, ainsi, la correspondance de motif est un peu comme "une instruction de commutation sur les stéréoïdes":
addFirstAndThird ajoutera volontiers le premier et le troisième élément des listes avec 2 comme deuxième élément, et sinon "passer" et "retourner" 0. Cette fonctionnalité "de type interrupteur" ne peut pas seulement être utilisée dans les définitions de fonction, par exemple:
De plus, il n'est pas limité aux listes, mais peut également être utilisé avec d'autres types, par exemple en faisant correspondre les constructeurs de valeur Just and Nothing du type Maybe afin de "dérouler" la valeur:
Bien sûr, ce n'étaient que de simples exemples de jouets, et je n'ai même pas essayé de donner une explication formelle ou exhaustive, mais ils devraient suffire à saisir le concept de base.
la source
Vous devriez commencer par la page Wikipédia qui donne une assez bonne explication. Ensuite, lisez le chapitre correspondant du wikibook Haskell .
C'est une belle définition du wikibook ci-dessus:
la source
Voici un exemple très court qui montre l'utilité de la correspondance de modèles:
Disons que vous souhaitez trier un élément dans une liste:
à (j'ai trié "New York")
dans un langage plus impératif vous écririez:
Dans un langage fonctionnel, vous écririez à la place:
Comme vous pouvez le voir, la solution correspondant au modèle a moins de bruit, vous pouvez clairement voir quels sont les différents cas et à quel point il est facile de voyager et de déstructurer notre liste.
J'ai écrit un article de blog plus détaillé à ce sujet ici .
la source