是一个函数,调用Math.random()纯?

下面是一个纯function吗?

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

我的理解是,一个纯粹的function遵循这些条件:

  1. 它返回从参数计算的值
  2. 除了计算返回值之外,它不做任何工作

如果这个定义是正确的,我的函数是一个纯函数吗? 或者,我的理解是什么定义一个纯函数是不正确的?

不,这不对。 给定相同的input,该函数将返回不同的值。 然后你不能build立一个映射input和输出的“表格”。

来自维基百科的纯粹function文章:

该函数总是在给定相同的参数值的情况下评估相同的结果值。 函数结果值不能取决于程序执行过程中或程序执行不同时可能改变的隐藏信息或状态,也不能依赖于来自I / O设备的任何外部input

另外,还有一件事是,一个纯函数可以用代表input输出映射的表来代替,就像这个线程所解释的那样。

如果要重写此函数并将其更改为纯函数,则也应将该随机值作为parameter passing

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

然后以这种方式调用它(例如,以2和5作为最小值和最大值):

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

你的问题的简单答案是Math.random()违反规则#2。

这里有很多其他的答案指出Math.random()的存在意味着这个函数不是纯粹的。 但是我认为值得说明的是为什么 Math.random()会使用它的函数。

像所有伪随机数生成器一样, Math.random()以“seed”值开始。 然后使用该值作为低级别位操作或其他操作链的起点,这些操作会导致不可预知(但不是真正的随机 )输出。

在JavaScript中,所涉及的过程是依赖于实现的,与许多其他语言不同,JavaScript 没有提供select种子的方法 :

实现将初始种子select为随机数生成algorithm; 它不能被用户select或重置。

这就是为什么这个函数不是纯粹的:JavaScript本质上是使用一个你无法控制的隐式函数参数。 它从计算并存储在别处的数据读取该参数,因此违反了定义中的规则#2。

如果你想使它成为一个纯函数,你可以使用这里描述的一个替代随机数生成器。 调用生成器seedable_random 。 它需要一个参数(种子)并返回一个“随机”数字。 当然,这个数字根本不是随机的, 它由种子独特地决定。 这就是为什么这是一个纯粹的function。 seedable_random的输出seedable_random是“随机的”,因为根据input来预测输出是困难的。

这个函数的纯版本需要三个参数:

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

对于任何给定的(min, max, seed)三元组,这将始终返回相同的结果。

请注意,如果你想让seedable_random的输出成为真正的随机数,你需要find一种随机化种子的方法! 无论你使用什么策略,都不可避免地会是非纯粹的,因为它会要求你从function之外的来源收集信息。 正如mtraceur和jpmc26提醒我的,这包括所有物理方法: 硬件随机数发生器 , 带镜头盖的摄像头 , 大气噪声收集器 – 甚至是熔岩灯 。 所有这些涉及使用计算和存储在函数外部的数据。

纯函数是一个函数,其中返回值仅由其input值确定,没有可观察到的副作用

通过使用Math.random,您可以通过input值以外的值来确定其值。 这不是一个纯粹的function。

资源

不,它不是一个纯函数,因为它的输出不仅仅取决于提供的input(Math.random()可以输出任何值),而纯函数应该总是为相同的input输出相同的值。

如果一个函数是纯粹的,那么使用相同的input来优化多个调用是安全的,只是重用先前调用的结果。

PS至less和其他许多人一样,REDX使得纯粹的function这个词stream行起来。 从redux文档直接 :

事情你永远不应该做一个减速机内:

  • 改变论点;

  • 执行API调用和路由转换等副作用;

  • 调用非纯函数,例如Date.now()或Math.random()。

从math的angular度来看,你的签名不是

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

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

environment能够提供Math.random()结果。 而实际上生成的随机值会将环境变为副作用,所以您还会返回一个不等于第一个环境的新环境!

换句话说,如果您需要任何不是来自初始参数( <number, number>部分)的input,那么您需要提供执行环境(在本例中为Math提供状态)。 其他答案提到的其他东西也是如此,例如I / O或其他。


作为一个类比,你也可以注意到这是如何面向对象的编程可以表示 – 如果我们说,例如

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

那么其实我们正在使用

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

其方法调用的对象是环境的一部分。 而为什么SomeClass一部分结果呢? 因为something的状态也可能改变了!

纯函数总是为相同的input返回相同的值。 纯函数是可预测的,并且是透明的,这意味着我们可以用返回的输出来replace函数调用,它不会改变程序的工作。

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

除了正确地指出这个函数是如何非确定性的其他答案之外,它还有一个副作用:它将导致将来的调用math.random()返回一个不同的答案。 而一个没有这个属性的随机数生成器通常会执行某种I / O,比如从OS提供的随机设备中读取。 要么是纯粹的function,要么就是这样。

不,不是的。 你根本无法弄清楚结果,所以这段代码不能被testing。 为了使代码可testing,您需要提取生成随机数的组件:

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

现在,你可以模拟生成器并正确testing你的代码:

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

在你的“生产”代码中:

 const result = test(1, 2, Math.random); 

你会没事的以下几点:

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

相当于

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

你看,pure的定义是一个函数,其输出不会随着input而改变。 如果我们说JavaScript有一种方法来标记一个纯函数并利用它,优化器将被允许将第一个expression式重写为第二个expression式。

我有这方面的实际经验。 SQL服务器允许在“纯”函数中使用getdate()newid() ,并且优化器可以newid()重复调用。 有时候这会做一些愚蠢的事情。