71 votes

Pourquoi un réducteur Redux est-il appelé un réducteur ?

Tout en apprenant Redux Je suis tombé sur Reducers . La documentation indique :

Le réducteur est une fonction pure qui prend l'état précédent et une action, et renvoie l'état suivant. (previousState, action) => newState. On l'appelle réducteur car c'est le type de fonction que l'on passe à Array.prototype.reduce(reducer, ?initialValue) .

MDN décrit le reduce méthode comme :

La méthode reduce() applique une fonction à un accumulateur et à chaque valeur du tableau (de gauche à droite) pour le réduire à une seule valeur.

Je ne comprends toujours pas pourquoi la définition Redux d'un réducteur n'a aucun sens. De plus, la description du MDN ne semble pas correcte non plus. Le site reduce n'est pas toujours utilisée pour réduire à une seule valeur. Elle peut être utilisée à la place de map y filter et est en fait plus rapide lorsqu'il est utilisé à la place du chaînage.

La description du MDN est-elle incorrecte ?

En revenant à la définition de Redux d'un réducteur, il est dit :

On l'appelle un réducteur parce que c'est le type de fonction que l'on passe à Array.prototype.reduce(reducer, ?initialValue).

J'ai l'impression qu'un réducteur dans Redux est responsable de la modification de l'état. Un exemple de reducer :

const count = function(state, action) {
    if(action.type == 'INCREMENT') {
        return state + 1;
    } else if(action.type == 'DECREMENT') {
        return state - 1;
    } else {
        return state;
    }
}

... Je ne vois pas en quoi c'est une fonction qui serait passée à reduce . Comment ces données sont-elles réduites à une seule valeur ? S'il s'agit d'une fonction que vous passeriez à reduce puis state serait le rappel et action serait la valeur initiale.

Merci pour toute explication claire. C'est difficile à conceptualiser.

9 votes

Excellente question !

0 votes

Pour moi, qui viens de WPF et de C#, les actions semblent être l'action "setter" et les réducteurs sont l'action "getter". Bien qu'il ne fonctionne pas réellement de cette façon sous le capot, car il renvoie un objet d'état complètement nouveau au lieu de le modifier, mais le résultat final est similaire à ce qu'il accomplit.

0 votes

En tant que développeur avec plus de 15 ans d'expérience en Java, .NET, Android, Angular, PHP, et d'autres piles, je peux dire avec certitude que les dénominations dans redux sont tout simplement une honte pour la communauté de développement. Les développeurs de redux devraient avoir honte de confondre des générations de développeurs.

58voto

jedd.ahyoung Points 2150

Le terme "réduire" est en fait un terme fonctionnel utilisé en programmation fonctionnelle. Dans un langage comme Haskell, F# ou même JavaScript, nous définissons une transformation qui prend une collection (de n'importe quelle taille) en entrée et renvoie une seule valeur en sortie.

Donc (sans vouloir être pédant, mais je trouve que cela m'aide) pensez-y visuellement. Nous avons une collection :

[][][][][][][][][][]

...que nous voulons réduire à une seule valeur :

N

En programmant de manière fonctionnelle, nous ferions cela avec une seule fonction que nous pourrions appeler de manière récursive sur chaque élément de la collection. Mais si vous faites cela, vous devez garder la trace de la valeur intermédiaire quelque part, n'est-ce pas ? Les implémentations non pures pourraient conserver une sorte d'"accumulateur" ou de variable en dehors de la fonction pour garder la trace de l'état, comme ceci :

var accumulator = 0;
var myArray = [1,2,3,4,5];

myArray.reduce(function (each) {
    accumulator += 0;
});

return accumulator;

Avec les fonctions pures, cependant, nous ne pouvons pas le faire - car par définition, les fonctions pures ne peuvent pas avoir d'effets en dehors de leur portée. Au lieu de s'appuyer sur une variable externe qui encapsule notre "état" entre les appels, nous transmettons simplement l'état dans la méthode :

var myArray = [1,2,3,4,5];

return myArray.reduce(function (accumulator, each) {
    return accumulator + each;
}, 0);

Dans ce cas, nous appelons la fonction un "réducteur" en raison de la signature de sa méthode. Nous avons each (ou current - n'importe quel nom convient), représentant un objet de la collection ; et state (ou previous ), qui est transmis à chaque itération de la fonction, représentant les résultats de la transformation que nous avons déjà effectuée sur les éléments précédents de la collection.

Notez que la documentation MDN que vous avez référencée est correcte ; la fonction reduce() renvoie toujours une seule valeur. En fait, la fonction reduce dans n'importe quel langage est une fonction d'ordre supérieur qui prend un "réducteur" (une fonction avec la signature de méthode définie ci-dessus) et renvoie une seule valeur. Maintenant, oui, vous peut faire d'autres choses avec, si la fonction que vous appelez a des effets secondaires, mais vous ne devriez pas. (Essentiellement, n'utilisez pas .reduce() comme un foreach). Même si la méthode que vous appelez avec reduce a des effets secondaires, le valeur de retour de réduire lui-même sera une valeur unique, et non une collection.

Ce qui est génial, c'est que ce modèle ne s'applique pas uniquement aux tableaux ou aux collections concrètes, comme vous l'avez vu dans React ; ce modèle peut également être appliqué aux flux, puisqu'il s'agit de fonctions pures.

J'espère que cela vous aidera. Pour ce que ça vaut, la définition sur le site de Redux pourrait être améliorée (car le concept de réducteur n'est pas seulement dû à la méthode Array prototype de Javascript). Vous devriez soumettre un PR !

Edit : Il y a un article Wikipedia sur le sujet. Notez que la réduction a différents noms, et dans les langages fonctionnels, elle est communément appelée Fold. https://en.wikipedia.org/wiki/Fold_(fonction d'ordre supérieur)#Plis_comme_transformations_structurelles

Modifier (2020-10-03) : Les gens semblent toujours trouver cela utile - c'est bien. Avec le temps, je me suis rendu compte que "fold" est un bien meilleur terme pour cela ; les langages fonctionnels l'ont bien compris. "Réducteur" n'est pas vraiment un mauvais terme, mais ce n'est pas nécessairement un bon terme non plus.

1 votes

"la valeur de retour de reduce itself sera une valeur unique, pas une collection". --- il faut reformuler cette phrase. Il n'y a rien de mal à retourner une collection. Fold est une opération plus puissante que n'importe quelle autre basée sur une collection, donc vous pouvez exprimer tout le reste avec juste ça.

0 votes

@zerkms Ehh, ouais, je suppose que tu as raison. Vous pourriez retourner une "collection", mais ... hmm. Je me demande quelle est la bonne façon de formuler cela. Je suppose que vous retournez l'état final de la transformation complète, qui est "un" état.

1 votes

C'est le pliage :-) (a -> b -> b) -> b -> [a] -> b --- voici une signature de haskell. Donc le type de retour b peut être n'importe quoi, y compris [a] . C'est pour cette raison que je n'aime pas trop le terme "réducteur", car il ne change jamais la forme de l'état (il peut le faire, mais seul un fou le ferait).

18voto

code4kix Points 956

La raison pour laquelle un réducteur redondant est appelé reducer c'est parce que vous pourriez "réduire" une collection of actions et un initial state (du magasin) sur lequel il faut effectuer ces actions pour obtenir le résultat de l'opération. final state .

Comment ? Pour répondre à cette question, laissez-moi définir à nouveau un réducteur :

En réduire() applique une méthode function (reducer) contre un accumulator et chaque valeur du tableau (de gauche à droite) pour le réduire à une seule valeur.

Et que fait un réducteur redux ?

Le réducteur est un pur function qui prend l'état actuel et un action, et renvoie l'état suivant. Notez que l'état est accumulated à mesure que chaque action sur la collection est appliquée pour changer cet état.

Ainsi, étant donné un collection of actions le réducteur est appliqué sur chaque valeur de la collection (de gauche à droite). La première fois, il retourne le initial value . Maintenant le réducteur est appliqué à nouveau sur cet état initial et la première action pour retourner l'état suivant. Et le réducteur suivant (action) est appliqué à chaque fois sur l'état initial. current state pour obtenir le next state jusqu'à ce qu'il atteigne la fin du tableau. Et ensuite, vous obtenez the final state . Comme c'est cool !

13voto

Black Akula Points 181

Désolé, mais je ne suis pas d'accord avec les réponses précédentes. I ne serait pas soutenir l'attribution de noms reducer . Je suis passionné par la FP et l'immuabilité. Ne me blâmez pas, lisez la deuxième partie, mais je veux d'abord expliquer pourquoi je ne suis pas d'accord.

Il est vrai que les réducteurs sont la séquence de transformations, mais la séquence elle-même - pourrait faire partie d'une autre séquence. Imaginez-la, comme des liens - une partie de la chaîne. Mais la chaîne elle-même pourrait faire partie d'une chaîne plus longue. Chaque lien est la "transition" de l'état global. Alors, quelle est la théorie derrière cela ?

Ne s'agit-il pas en fait de la "Finite state machine" ? - proche, mais non. C'est en fait la Système de transition .

Un système de transition étiqueté est un tuple (S, , ) où S est un ensemble d'états, est un ensemble d'étiquettes et est un ensemble de transitions étiquetées.

Donc, S - sont l'ensemble de nos états

- est notre soi-disant "action" (mais étiquettes en théorie)

... et

- réducteurs "transitions labellisées" ! Je la nommerais ainsi, si je suis le créateur de cette bibliothèque.

Comprendre cette théorie m'a aidé à mettre en place ma bibliothèque, où je peux avoir système de transition de bas niveau dans le cadre de système de transition de haut niveau (comme une chaîne - qui pourrait toujours faire partie d'une chaîne plus longue) - et toujours avoir un seul état global Redux.

8 votes

J'ai eu du mal avec la bibliothèque simplement parce que je n'arrive pas à dépasser la nomenclature 'reducer'... c'est en fait utile pour moi de savoir que d'autres ont aussi du mal avec ce nom. La définition de @code4kix ci-dessus m'a également aidé

4voto

naomik Points 10423

J'ai l'impression qu'un réducteur dans Redux est responsable de la modification de l'état. Un exemple de reducer :

const count = function(state, action) {
    if (action.type == 'INCREMENT') {
        return state + 1;
    } else if (action.type == 'DECREMENT') {
        return state - 1;
    } else {
        return state;
    }
}

... Je ne vois pas en quoi c'est une fonction qui serait passée à reduce. Comment ces données sont-elles réduites à une seule valeur ? S'il s'agit d'une fonction que vous transmettez à reduce, alors state serait le callback et action serait la valeur initiale.

// count function from your question
const count = function (state, action) {
    if (action.type == 'INCREMENT') {
        return state + 1;
    } else if (action.type == 'DECREMENT') {
        return state - 1;
    } else {
        return state;
    }
}

// an array of actions
const actions =
  [ { type: 'INCREMENT' }
  , { type: 'INCREMENT' }
  , { type: 'INCREMENT' }
  , { type: 'INCREMENT' }
  , { type: 'DECREMENT' }
  ]

// initial state
const init = 0

console.log(actions.reduce(count, init))
// 3 (final state)
// (INCREMENT 4 times, DECREMENT 1 time)

2voto

DTing Points 12969

On l'appelle un réducteur parce que c'est le type de fonction que l'on passe à Array.prototype.reduce(reducer, ?initialValue).

Array.reduce

C'est très similaire à ce que vous passeriez à Array.reduce comme callback (réducteur). La partie importante étant :

callback
  Function to execute on each value in the array, taking four arguments:
    previousValue
      The value previously returned in the last invocation of the callback, or initialValue, if supplied. (See below.)
    currentValue
      The current element being processed in the array.

Où state est la "previousValue" et action est la "currentValue".

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X