Les transducteurs sont des recettes sur ce qu'il faut faire avec une séquence de données sans savoir quelle est la séquence sous-jacente (comment le faire). Il peut s'agir de n'importe quel canal séquentiel, asynchrone ou peut-être observable.
Ils sont composables et polymorphes.
L'avantage est que vous n'avez pas à implémenter tous les combinateurs standard à chaque fois qu'une nouvelle source de données est ajoutée. Encore et encore. En tant qu'utilisateur, vous pouvez réutiliser ces recettes sur différentes sources de données.
Mise à jour de l'annonce
Avant la version 1.7 de Clojure, vous disposiez de trois façons d'écrire des requêtes de flux de données:
- appels imbriqués
(reduce + (filter odd? (map #(+ 2 %) (range 0 10))))
- composition fonctionnelle
(def xform
(comp
(partial filter odd?)
(partial map #(+ 2 %))))
(reduce + (xform (range 0 10)))
- macro de filetage
(defn xform [xs]
(->> xs
(map #(+ 2 %))
(filter odd?)))
(reduce + (xform (range 0 10)))
Avec des transducteurs, vous l'écrivez comme:
(def xform
(comp
(map #(+ 2 %))
(filter odd?)))
(transduce xform + (range 0 10))
Ils font tous la même chose. La différence est que vous n'appelez jamais directement les transducteurs, vous les passez à une autre fonction. Les transducteurs savent quoi faire, la fonction qui obtient le transducteur sait comment. L'ordre des combinateurs est comme vous l'écrivez avec une macro de threading (ordre naturel). Vous pouvez maintenant réutiliser xform
avec le canal:
(chan 1 xform)
Les transducteurs améliorent l'efficacité et vous permettent d'écrire du code efficace de manière plus modulaire.
C'est un parcours décent .
Par rapport à la composition des appels à l'ancienne
map
,filter
,reduce
etc. vous obtenez une meilleure performance parce que vous ne avez pas besoin de construire des collections intermédiaires entre chaque étape, et à plusieurs reprises ces collections marcher.Par rapport à
reducers
, ou en composant manuellement toutes vos opérations en une seule expression, vous obtenez plus facilement des abstractions, une meilleure modularité et une réutilisation des fonctions de traitement.la source
map
/reduce
d'utiliser des collections intermédiaires car tous construisent une chaîne d'itérateurs. Où ai-je tort ici?map
etfilter
crée des collections intermédiaires lorsqu'elles sont imbriquées.Les transducteurs sont un moyen de combinaison pour réduire les fonctions.
Exemple: les fonctions de réduction sont des fonctions qui prennent deux arguments: un résultat jusqu'à présent et une entrée. Ils renvoient un nouveau résultat (pour l'instant). Par exemple
+
: avec deux arguments, vous pouvez considérer le premier comme le résultat jusqu'à présent et le second comme l'entrée.Un transducteur peut maintenant prendre la fonction + et en faire une fonction deux fois plus (double chaque entrée avant de l'ajouter). Voici à quoi ressemblerait ce transducteur (dans les termes les plus élémentaires):
Pour l'illustration, remplacez
rfn
par+
pour voir comment+
se transforme en deux fois plus:Alors
donnerait maintenant 12.
Les fonctions de réduction renvoyées par les transducteurs sont indépendantes de la façon dont le résultat est accumulé car elles s'accumulent avec la fonction de réduction qui leur est transmise, sans le savoir comment. Ici, nous utilisons à la
conj
place de+
.Conj
prend une collection et une valeur et retourne une nouvelle collection avec cette valeur ajoutée.donnerait [2 4 6]
Ils sont également indépendants du type de source d'entrée.
Plusieurs transducteurs peuvent être chaînés en tant que recette (chaînable) pour transformer les fonctions réductrices.
Mise à jour: Puisqu'il existe maintenant une page officielle à ce sujet, je recommande vivement de la lire: http://clojure.org/transducers
la source
double
ettransduce
?Supposons que vous souhaitiez utiliser une série de fonctions pour transformer un flux de données. Le shell Unix vous permet de faire ce genre de chose avec l'opérateur pipe, par exemple
(La commande ci-dessus compte le nombre d'utilisateurs avec la lettre r en majuscule ou en minuscule dans leur nom d'utilisateur). Ceci est implémenté comme un ensemble de processus, dont chacun lit à partir de la sortie des processus précédents, il y a donc quatre flux intermédiaires. Vous pouvez imaginer une implémentation différente qui compose les cinq commandes en une seule commande d'agrégation, qui lirait à partir de son entrée et écrirait sa sortie exactement une fois. Si les flux intermédiaires étaient chers et la composition bon marché, cela pourrait être un bon compromis.
Le même genre de chose vaut pour Clojure. Il existe plusieurs façons d'exprimer un pipeline de transformations, mais selon la façon dont vous le faites, vous pouvez vous retrouver avec des flux intermédiaires passant d'une fonction à l'autre. Si vous avez beaucoup de données, il est plus rapide de composer ces fonctions en une seule fonction. Les transducteurs facilitent cette tâche. Une innovation Clojure antérieure, les réducteurs, vous permet de le faire aussi, mais avec quelques restrictions. Les transducteurs suppriment certaines de ces restrictions.
Donc, pour répondre à votre question, les transducteurs ne rendront pas nécessairement votre code plus court ou plus compréhensible, mais votre code ne sera probablement pas plus long ou moins compréhensible non plus, et si vous travaillez avec beaucoup de données, les transducteurs peuvent rendre votre code plus rapide.
C'est un assez bon aperçu des transducteurs.
la source
pmap
, ce qui ne semble pas attirer suffisamment l'attention. Si vous envoyezmap
un ping à une fonction coûteuse sur une séquence, rendre l'opération parallèle est aussi simple que d'ajouter "p". Pas besoin de changer quoi que ce soit d'autre dans votre code, et il est disponible maintenant - pas alpha, pas bêta. (Si la fonction crée des séquences intermédiaires, les transducteurs pourraient être plus rapides, je suppose.)Rich Hickey a donné une conférence «Transducers» lors de la conférence Strange Loop 2014 (45 min).
Il explique de manière simple ce que sont les transducteurs, avec des exemples du monde réel - le traitement des sacs dans un aéroport. Il sépare clairement les différents aspects et les met en contraste avec les approches actuelles. Vers la fin, il donne la justification de leur existence.
Vidéo: https://www.youtube.com/watch?v=6mTbuzafcII
la source
J'ai trouvé que la lecture d'exemples de transducers-js m'aide à les comprendre concrètement sur la façon dont je pourrais les utiliser dans le code au jour le jour.
Par exemple, considérons cet exemple (tiré du README au lien ci-dessus):
D'une part, l'utilisation
xf
semble beaucoup plus propre que l'alternative habituelle avec Underscore.la source
t.into([], t.comp(t.map(inc), t.filter(isEven)), [0,1,2,3,4])
Les transducteurs sont (à ma connaissance!) Des fonctions qui prennent une fonction réductrice et en renvoient une autre. Une fonction réductrice est celle qui
Par exemple:
Dans ce cas mon-transducteur prend une fonction de filtrage d'entrée qu'il applique à 0 alors si cette valeur est paire? dans le premier cas, le filtre transmet cette valeur au compteur, puis il filtre la valeur suivante. Au lieu de filtrer d'abord, puis de passer toutes ces valeurs pour compter.
C'est la même chose dans le deuxième exemple, il vérifie une valeur à la fois et si cette valeur est inférieure à 3, alors il laisse compter ajouter 1.
la source
Une définition claire du transducteur est ici:
Pour le comprendre, considérons l'exemple simple suivant:
Qu'en est-il que nous voulons savoir combien d'enfants il y a dans le village? Nous pouvons facilement le trouver avec le réducteur suivant:
Voici une autre façon de le faire:
De plus, il est vraiment puissant lors de la prise en compte des sous-groupes. Par exemple, si nous voulons savoir combien d'enfants sont dans la famille Brown, nous pouvons exécuter:
J'espère que vous trouverez ces exemples utiles. Vous pouvez en trouver plus ici
J'espère que ça aide.
Clemencio Morales Lucas.
la source
J'ai blogué à ce sujet avec un exemple clojurescript qui explique comment les fonctions de séquence sont maintenant extensibles en pouvant remplacer la fonction de réduction.
C'est le but des transducteurs tel que je le lis. Si vous pensez à l' opération
cons
ouconj
qui est codée en dur dans des opérations telles quemap
,filter
etc., la fonction de réduction était inaccessible.Avec les transducteurs, la fonction réductrice est découplée et je peux la remplacer comme je l'ai fait avec le tableau natif javascript
push
grâce à des transducteurs.filter
et les amis ont une nouvelle opération 1 arity qui renverra une fonction de transduction que vous pouvez utiliser pour fournir votre propre fonction de réduction.la source
Voici ma réponse (principalement) sans jargon et sans code.
Pensez aux données de deux manières, un flux (des valeurs qui surviennent au fil du temps comme des événements) ou une structure (des données qui existent à un moment donné comme une liste, un vecteur, un tableau, etc.).
Vous pouvez souhaiter effectuer certaines opérations sur des flux ou des structures. Une de ces opérations est la cartographie. Une fonction de mappage peut incrémenter chaque élément de données (en supposant qu'il s'agit d'un nombre) de 1 et vous pouvez, espérons-le, imaginer comment cela pourrait s'appliquer à un flux ou à une structure.
Une fonction de mappage n'est qu'une des classes de fonctions parfois appelées "fonctions réductrices". Une autre fonction de réduction courante est le filtre qui supprime les valeurs qui correspondent à un prédicat (par exemple, supprime toutes les valeurs paires).
Les transducteurs vous permettent de "envelopper" une séquence d'une ou plusieurs fonctions réductrices et de produire un "package" (qui est lui-même une fonction) qui fonctionne à la fois sur les flux ou les structures. Par exemple, vous pouvez «empaqueter» une séquence de fonctions réductrices (par exemple, filtrer les nombres pairs, puis mapper les nombres résultants pour les incrémenter de 1), puis utiliser ce «paquet» de transducteur sur un flux ou une structure de valeurs (ou les deux) .
Alors, quelle est la particularité de cela? En règle générale, les fonctions de réduction ne peuvent pas être efficacement composées pour fonctionner à la fois sur les flux et les structures.
L'avantage pour vous est que vous pouvez tirer parti de vos connaissances sur ces fonctions et les appliquer à plus de cas d'utilisation. Le coût pour vous est que vous devez apprendre quelques machines supplémentaires (par exemple le transducteur) pour vous donner cette puissance supplémentaire.
la source
Pour autant que je sache, ils sont comme des blocs de construction , découplés de l'implémentation d'entrée et de sortie. Vous définissez simplement l'opération.
Comme la mise en œuvre de l'opération n'est pas dans le code de l'entrée et que rien n'est fait avec la sortie, les transducteurs sont extrêmement réutilisables. Ils me rappellent les Flow dans Akka Streams .
Je suis également nouveau dans les transducteurs, désolé pour la réponse peut-être peu claire.
la source
Je trouve que cet article vous donne une vue plus à vol d'oiseau du transducteur.
https://medium.com/@roman01la/understanding-transducers-in-javascript-3500d3bd9624
la source