web-dev-qa-db-fra.com

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

Est-ce que ce qui suit est une fonction pure?

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

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

  1. Il retourne une valeur calculée à partir des paramètres
  2. Il ne fait aucun travail autre que le calcul de la valeur de retour

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

109
Kiwi Rupela

Non ce n'est pas. Avec la même entrée, cette fonction retournera des valeurs différentes. Et puis vous ne pouvez pas construire un "tableau" qui mappe l'entrée et les sorties.

De l'article de Wikipedia pour fonction pure :

La fonction évalue toujours la même valeur de résultat avec les mêmes valeurs d'argument. La valeur du résultat de la fonction ne peut dépendre d'aucune information cachée ni d'aucun état susceptible de changer pendant l'exécution du programme ou entre différentes exécutions du programme, ni d'une entrée externe provenant de périphériques d'E/S.

En outre, une autre fonction est qu'une fonction pure peut être remplacée par une table représentant le mappage de l'entrée et de la sortie, comme expliqué dans ce fil .

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

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

puis appelez-le de cette façon (exemple, avec 2 et 5 min et max):

test( Math.random(), 2, 5)
179
Christian Benseler

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

Beaucoup d'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() altère 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 pour une chaîne de manipulations de bits de bas niveau ou d'autres opérations aboutissant à une sortie imprévisible (mais pas vraiment aléatoire ).

En JavaScript, le processus impliqué dépend de la mise en œuvre et contrairement à de nombreux autres langages, JavaScript fournit aucun moyen de sélectionner la valeur de départ :

La mise en œuvre sélectionne le germe initial de l'algorithme de génération de nombres aléatoires; il ne peut pas être choisi ou réinitialisé 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 enfreint donc la règle n ° 2 de votre définition.

Si vous voulez en faire une fonction pure, vous pouvez utiliser l’un des générateurs alternatifs de nombres aléatoires décrits ici . Appelez ce générateur seedable_random. Il prend un paramètre (la graine) et retourne un nombre "aléatoire". Bien sûr, ce nombre n’est pas vraiment aléatoire; il est uniquement déterminé par la graine. C'est pourquoi c'est une fonction pure. La sortie de seedable_random est uniquement "aléatoire" 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;
}

Quel que soit le triple des paramètres (min, max, seed), le résultat sera toujours le même.

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

49
senderle

Une fonction pure est une fonction où 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 des valeurs d'entrée. Ce n'est pas une fonction pure.

source

38
TKoL

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

Si une fonction est pure, vous pouvez optimiser plusieurs appels avec les mêmes entrées et réutiliser le résultat d'un appel précédent.

P.S pour moi au moins et pour beaucoup d’autres, redux a rendu populaire le terme , fonction pure (-) . directement des documents redux :

Choses que vous ne devriez jamais faire à l'intérieur d'un réducteur:

  • Mutate ses arguments;

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

  • Appelez des fonctions non pures, par ex. Date.now () ou Math.random ().

25
Shubhnik Singh

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

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

mais

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

où la environment est capable de fournir les résultats de Math.random(). Et générer réellement la valeur aléatoire provoque la mutation de l'environnement en tant qu'effet secondaire. Vous renvoyez donc 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 d'arguments initiaux (la partie <number, number>), vous devez disposer d'un environnement d'exécution (dans cet exemple, l'état de Math est fourni. ). La même chose s'applique à d'autres choses mentionnées par d'autres réponses, comme les entrées/sorties ou autres.


Par analogie, vous pouvez également remarquer que c’est ainsi que la programmation orientée objet peut être représentée - si l’on dit, par exemple.

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

alors en fait, nous utilisons

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

avec l'objet dont la méthode est invoquée faisant partie de l'environnement. Et pourquoi la SomeClass partie du résultat? Parce que l'état de something aurait également pu changer!

20
Adam Kotwasinski

Les fonctions pures retournent toujours la même valeur pour la même entrée. Les fonctions pures sont prévisibles et sont transparentes référentielles, ce qui signifie que nous pouvons remplacer l'appel de fonction par la sortie renvoyée et que cela ne changera pas le fonctionnement du programme.

https://github.com/MostlyAdequate/mostly- Inappropriate-guide/blob/master/ch3.md

11
Rishabh Mishra

Outre les autres réponses qui soulignent correctement le fait que cette fonction est non déterministe, elle a également un effet secondaire: les futurs appels à math.random() renverront une réponse différente. Et un générateur de nombre aléatoire qui ne possède pas cette propriété effectuera généralement une sorte d’E/S, par exemple pour lire à partir d’un périphérique aléatoire fourni par le système d’exploitation. L'un ou l'autre est verboten pour une fonction pure.

9
Davislor

Non ce n'est pas. Vous ne pouvez pas comprendre le résultat du tout, donc ce morceau de code ne peut pas être testé. Pour que ce code soit testable, vous devez extraire le composant qui génère le nombre aléatoire:

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

Maintenant, vous pouvez vous moquer du générateur et tester votre code correctement:

const result = test(1, 2, () => 3);
result == 4 //always true

Et dans votre code "production":

const result = test(1, 2, Math.random);
7
Héctor

Seriez-vous d'accord avec ce qui suit:

return ("" + test(0,1)) + test(0,1);

être équivalent à

var temp = test(0, 1);
return ("" + temp) + temp;

?

Vous voyez, la définition de pure est une fonction dont la sortie ne change pas avec autre chose que ses entrées. Si nous disons que JavaScript permet de baliser une fonction pure et d'en tirer parti, l'optimiseur serait autorisé à réécrire la première expression en tant que seconde.

J'ai une expérience pratique avec cela. Serveur SQL autorisé getdate() et newid() dans les fonctions "pures" et l'optimiseur dédupliquerait les appels à volonté. Parfois, cela ferait quelque chose de stupide.

2
Joshua