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

时间:2017-10-31 12:14:43

标签: javascript function pure-function

以下是纯函数吗?

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

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

  1. 它返回根据参数
  2. 计算的值
  3. 除了计算返回值
  4. 之外,它不做任何其他工作

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

9 个答案:

答案 0 :(得分:178)

不,不是。给定相同的输入,此函数将返回不同的值。然后你就无法建立一个映射输入和输出的'表'。

来自维基百科的Pure function文章:

  

该函数始终评估相同的结果值   参数值。功能结果值不能依赖于任何   程序执行时可能发生变化的隐藏信息或状态   继续或在程序的不同执行之间,也不可能   取决于I / O设备的任何外部输入

另外,另一件事是纯函数可以用表来代替输入和输出的映射,如this thread中所述。

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

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

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

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

答案 1 :(得分:49)

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

这里的许多其他答案都指出Math.random()的存在意味着这个功能并不纯粹。但我认为值得说明为什么 Math.random()使用它的功能。

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

在JavaScript中,涉及的过程依赖于实现,与许多其他语言不同,JavaScript提供no way to select the seed

  

实现选择初始种子到随机数生成算法;它不能被用户选择或重置。

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

如果你想让它成为一个纯函数,你可以使用here描述的替代随机数生成器之一。调用该生成器seedable_random。它需要一个参数(种子)并返回一个“随机”数字。当然,这个数字根本不是随机的;它由种子决定。这就是为什么这是一个纯粹的功能。 seedable_random的输出只是“随机”,因为根据输入预测输出很困难。

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

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

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

请注意,如果您希望seedable_random的输出真正随机,那么您需要找到一种随机化种子的方法!无论你使用什么策略都不可避免地是非纯粹的,因为它需要你从你职能之外的来源收集信息。正如mtraceurjpmc26提醒我的那样,这包括所有物理方法:hardware random number generatorswebcams with lens capsatmospheric noise collectors - 偶数lava lamps。所有这些都涉及使用计算并存储在函数外部的数据。

答案 2 :(得分:38)

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

通过使用Math.random,您可以通过输入值以外的其他值来确定其值。这不是一个纯粹的功能。

source

答案 3 :(得分:25)

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

如果某个函数是纯函数,那么使用相同的输入优化掉多个调用是安全的,只需重用之前调用的结果即可。

对我来说至少和其他许多人一样,redux使术语 纯粹的功能 很受欢迎。 Straight from the redux docs

  

你应该在减速机内做的事情:

     
      
  • 改变其论点;

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

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

  •   

答案 4 :(得分:20)

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

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

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

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

换句话说,如果您需要任何类型的输入不是来自初始参数(<number, number>部分),那么您需要提供执行环境(在此示例中为{{{ 1}})。这同样适用于其他答案提到的其他事项,例如I / O等。

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

Math

然后实际上我们正在使用

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

将调用其方法的对象作为环境的一部分。为什么结果的foo: <something: SomeClass, x: Object, y: Object> -> <SomeClass, T> 部分?因为SomeClass的状态也可以改变!

答案 5 :(得分:11)

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

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

答案 6 :(得分:9)

除了正确指出此函数是如何非确定性的其他答案之外,它还有副作用:它将导致将来调用math.random()以返回不同的答案。并且具有该属性的随机数生成器通常将执行某种I / O,例如从OS提供的随机设备读取。要么是纯粹的功能,要么禁止使用。

答案 7 :(得分:7)

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

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

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

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

在你的&#34;生产&#34;代码:

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

答案 8 :(得分:2)

您可以使用以下内容:

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

相当于

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

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

我有这方面的实践经验。 SQL服务器在“纯”函数中允许getdate()newid(),优化器会随意重复调用。有时这会让事情变得愚蠢。