111 votes

Une fonction qui appelle Math.random() est-elle pure ?

Le texte suivant est-il une fonction pure ?

function test(min,max) {
   return  Math.random() * (max - min) + min;
}

Je crois savoir qu'une fonction pure respecte ces conditions :

  1. Il retourne une valeur calculée à partir des paramètres
  2. Il ne fait rien d'autre que de calculer la valeur de retour.

Si cette définition est correcte, ma fonction est-elle une fonction pure ? Ou ma compréhension de ce qui définit une fonction pure est-elle incorrecte ?

184voto

Christian Benseler Points 4786

Non, ça ne l'est pas. Avec la même entrée, cette fonction renverra des valeurs différentes. Et alors vous ne pouvez pas construire une "table" qui relie les entrées et les sorties.

Extrait de l'article de Wikipédia sur Fonction pure :

La fonction évalue toujours la même valeur de résultat pour un même valeur(s) d'argument. La valeur du résultat de la fonction ne peut dépendre d'aucune d'une information cachée ou d'un état qui peut changer pendant l'exécution du programme programme ou entre différentes exécutions du programme, et ne peut pas non plus dépendre d'une entrée externe provenant de périphériques d'E/S

De plus, une fonction pure peut être remplacée par un tableau qui représente le mappage entre l'entrée et la sortie, comme expliqué dans le document suivant ce fil .

Si vous voulez réécrire cette fonction et la transformer en une fonction pure, vous devez également passer la valeur aléatoire comme argument.

function test(random, min, max) {
   return random * (max - min) + min;
}

et l'appeler de cette façon (exemple, avec 2 et 5 comme min et max) :

test( Math.random(), 2, 5)

50voto

senderle Points 41607

La réponse simple à votre question est que Math.random() viole la règle n°2.

De nombreuses autres réponses ici ont souligné que la présence de Math.random() signifie que cette fonction n'est pas pure. Mais je pense que cela vaut la peine de dire pourquoi Math.random() entache les fonctions qui l'utilisent.

Comme tous les générateurs de nombres pseudo-aléatoires, Math.random() commence par une valeur "de départ". Il utilise ensuite cette valeur comme point de départ d'une chaîne de manipulations de bits de bas niveau ou d'autres opérations qui aboutissent à un résultat imprévisible (mais pas vraiment). au hasard ).

En JavaScript, le processus impliqué dépend de l'implémentation, et contrairement à de nombreux autres langages, JavaScript fournit aucun moyen de sélectionner la graine :

L'implémentation sélectionne la graine initiale de l'algorithme de génération de nombres aléatoires ; elle ne peut pas être choisie ou réinitialisée par l'utilisateur.

C'est pourquoi cette fonction n'est pas pure : JavaScript utilise essentiellement un paramètre de fonction implicite sur lequel vous n'avez aucun contrôle. Il lit ce paramètre à partir de données calculées et stockées ailleurs, et viole donc la règle n° 2 de votre définition.

Si vous vouliez en faire une fonction pure, vous pourriez utiliser l'un des générateurs de nombres aléatoires alternatifs décrits aquí . Appelons ce générateur seedable_random . Il prend un paramètre (la graine) et renvoie un nombre "aléatoire". Bien sûr, ce nombre n'est pas vraiment aléatoire ; il est déterminé de façon unique par la graine. C'est pourquoi il s'agit d'une fonction pure. La sortie de seedable_random n'est "aléatoire" que dans le sens où il est difficile de prédire la sortie en fonction de l'entrée.

La version pure de cette fonction devrait prendre trois paramètres :

function test(min, max, seed) {
   return  seedable_random(seed) * (max - min) + min;
}

Pour tout triplet donné de (min, max, seed) cette opération renverra toujours le même résultat.

Notez que si vous voulez la sortie de seedable_random à être vraiment aléatoire, il faudrait trouver un moyen de rendre la graine aléatoire ! Et quelle que soit la stratégie que vous utilisiez, elle serait inévitablement non pure, car elle vous obligerait à recueillir des informations d'une source extérieure à votre fonction. Comme mtraceur y jpmc26 Pour moi, cela inclut toutes les approches physiques : générateurs de nombres aléatoires matériels , webcams avec bouchons d'objectif , les collecteurs de bruit atmosphérique -- même lampes à lave . Tous ces éléments impliquent l'utilisation de données calculées et stockées en dehors de la fonction.

38voto

TKoL Points 2804

Une fonction pure est une fonction dont la valeur de retour est uniquement déterminée par ses valeurs d'entrée, sans effets secondaires observables.

En utilisant Math.random, vous déterminez sa valeur par autre chose que les valeurs d'entrée. Il ne s'agit pas d'une fonction pure.

source

25voto

Shubhnik Singh Points 1123

Non, ce n'est pas une fonction pure car sa sortie ne dépend pas sólo en fonction de l'entrée fournie (Math.random() peut produire n'importe quelle valeur), alors que les fonctions pures doivent toujours produire la même valeur pour les mêmes entrées.

Si une fonction est pure, il est possible d'optimiser les appels multiples avec les mêmes entrées et de réutiliser simplement le résultat d'un appel antérieur.

P.S pour moi au moins et pour beaucoup d'autres, redux a fait le terme pure fonction populaire. Tout droit sorti de la documentation de Redux :

Les choses que vous ne devez jamais faire à l'intérieur d'un réducteur :

  • Mutation de ses arguments ;

  • Effectuer des effets secondaires comme des appels d'API et des transitions de routage ;

  • Appeler des fonctions non pures, par exemple Date.now() ou Math.random().

20voto

Adam Kotwasinski Points 1584

D'un point de vue mathématique, votre signature n'est pas

test: <number, number> -> <number>

mais

test: <environment, number, number> -> <environment, number>

où le environment est capable de fournir des résultats de Math.random() . Et la génération de la valeur aléatoire a pour effet secondaire de faire muter l'environnement, de sorte que vous renvoyez également un nouvel environnement, qui n'est pas égal au premier !

En d'autres termes, si vous avez besoin d'un type d'entrée qui ne provient pas des arguments initiaux (l'élément <number, number> ), vous devez disposer d'un environnement d'exécution (qui, dans cet exemple, fournit l'état pour les éléments suivants Math ). Il en va de même pour d'autres choses mentionnées dans d'autres réponses, comme les E/S ou autres.


Par analogie, vous pouvez également remarquer que c'est ainsi que la programmation orientée objet peut être représentée - si nous disons, par exemple, que

SomeClass something
T result = something.foo(x, y)

alors en fait nous utilisons

foo: <something: SomeClass, x: Object, y: Object> -> <SomeClass, T>

l'objet dont la méthode est invoquée faisant partie de l'environnement. Et pourquoi le SomeClass partie du résultat ? Parce que something L'état de l'entreprise pourrait également avoir changé !

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