javascript – 是一个调用Math.random()纯函数的函数吗?

Is a function that calls Math.random() pure?

以下是纯函数吗?

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

我的理解是纯函数遵循以下条件:

  • 它返回根据参数计算的值
  • 除了计算返回值外,它不做任何工作
  • 如果这个定义正确,我的函数是纯函数吗?或者我对纯函数定义的理解是错误的?


    不,不是。如果输入相同,此函数将返回不同的值。然后,您就不能构建一个映射输入和输出的"表"。

    从维基百科的纯函数文章:

    The function always evaluates the same result value given the same
    argument value(s). The function result value cannot depend on any
    hidden information or state that may change while program execution
    proceeds or between different executions of the program, nor can it
    depend on any external input from I/O devices

    另外,另一件事是纯函数可以替换为表示输入和输出映射的表,如本线程中所述。

    如果要重写此函数并将其更改为纯函数,还应将随机值作为参数传递

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

    然后这样称呼它(例如,最小值和最大值分别为2和5):

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


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

    这里还有许多其他的答案指出,Math.random()的存在意味着这个函数并不纯粹。但我认为值得一提的是,为什么Math.random()会污染使用它的函数。

    和所有伪随机数生成器一样,Math.random()以"seed"值开头。然后,它使用该值作为一系列低级位操作或其他操作的起点,这些操作会导致不可预测(但不是真正随机)的输出。

    在javascript中,所涉及的过程依赖于实现,与许多其他语言不同,javascript无法选择种子:

    The implementation selects the initial seed to the random number generation algorithm; it cannot be chosen or reset by the user.

    这就是为什么这个函数不是纯函数:JavaScript本质上使用的是一个您无法控制的隐式函数参数。它从其他地方计算和存储的数据中读取该参数,因此违反了定义中的规则2。

    如果你想让它成为一个纯函数,你可以使用这里描述的另一个随机数生成器。叫那个发电机seedable_random。它接受一个参数(种子)并返回一个"随机"数字。当然,这个数字根本不是随机的,它是由种子决定的。这就是为什么这是一个纯函数。seedable_random的输出只是"随机的",从这个意义上说,根据输入预测输出是困难的。

    此函数的纯版本需要采用三个参数:

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

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

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


    纯函数是一个函数,其中返回值仅由其输入值确定,没有明显的副作用。

    通过使用math.random,您可以通过输入值以外的其他值来确定其值。它不是纯函数。

    来源


    不,它不是纯函数,因为它的输出不仅依赖于提供的输入(math.random()可以输出任何值),而纯函数应该始终为相同的输入输出相同的值。

    如果一个函数是纯函数,那么优化多个具有相同输入的调用是安全的,只需重用先前调用的结果。

    至少对我和其他许多人来说,redux使纯函数这个词很流行。直接从Redux文档:

    Things you should never do inside a reducer:

    • Mutate its arguments;

    • Perform side effects like API calls and routing transitions;

    • Call non-pure functions, e.g. Date.now() or Math.random().


    从数学的角度看,你的签名不是

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

    但是

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

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

    换句话说,如果您需要任何不是来自初始参数的输入(部分),那么需要为您提供执行环境(在本例中,该环境为Math提供状态)。这同样适用于其他答案提到的其他事情,如I/O或类似的。

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

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

    实际上我们正在使用

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

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


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

    https://github.com/mostly adequate/mostly-sufficient-guide/blob/master/ch3.md网站


    除了正确指出该函数是如何非确定性的其他答案外,它还有一个副作用:它将导致将来调用Math.random()返回不同的答案。而一个没有这个属性的随机数生成器通常会执行某种类型的I/O,比如从操作系统提供的随机设备中读取。两者都是纯函数的原语。


    不,不是的。你根本不知道结果,所以这段代码无法测试。要使该代码可测试,需要提取生成随机数的组件:

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

    现在,您可以模拟生成器并正确测试代码:

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

    在您的"生产"代码中:

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


    您能接受以下条件吗?

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

    相当于

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

    你看,pure的定义是一个函数,它的输出除了它的输入外没有任何变化。如果我们说javascript有一种方法可以标记一个函数pure并利用它,优化器将允许将第一个表达式重写为第二个表达式。

    我在这方面有实际经验。SQL Server允许getdate()newid()使用"pure"函数,优化器将自动消除调用。有时这会做一些愚蠢的事情。