为什么Today()是不纯函数的示例?


38

看起来,当阅读类似于Wikipedia的有关“纯函数”的文章时,它们被Today()列为不纯函数的示例,但对我来说似乎很纯净。是否因为没有正式的输入参数?为什么在一天中的实际时间不被视为“函数的输入”,在这种情况下,如果您给它相同的输入,即一次执行today()两次,或者又返回原点以再次执行它(可能是一个假设: )),输出将是同一时间。Today()永远不会给您一个随机数。它总是给您一天中的时间。

维基百科的文章说“不同的时间会产生不同的结果”,但这就像说不同x sin(x)会给您带来不同的比率。并且sin(x)是他们纯函数的例子。


8
如果您在一天中的某个时间通过,该函数会做什么?
JB King

1
我希望它能给您一天的时间。(不是最有用的功能)。但这没有任何论据,我认为这是答案的根源。
Brad

3
您可以预测其输出(基于您提供的输入参数)吗?
Daniel B

1
@DanielB事实证明,缺少/空输入参数没有预测能力。我唯一能做的就是看手表(我的手机)。
Brad

“为什么一天中的实际时间不被视为“功能的输入””,基本上,这是monads试图解决的问题。纯函数只能基于其输入,并且没有副作用。如果将“我之前的世界状态”作为输入,并将“我之后的世界状态”作为返回值的一部分,并通过程序传递这些世界状态,则可以再次变得纯净。
肖恩·麦克索明

Answers:


103

是否因为没有正式的输入参数?

这是因为输出取决于不是输入的东西,即当前时间。

为什么一天中的实际时间不被视为“功能输入”

因为您没有将其作为参数传递。如果确实将其作为参数传递,则该函数将成为日期上的标识函数,这几乎没有用。一个的全部点Today()的功能是输出的东西,依赖于外部和不断变化的值(时间)。

纯函数的优点是它们的行为是绝对可复制的和确定性的,从而使获得正式证明和硬保证变得容易。他们总是做同样的事情。Today()恰恰相反:它总是(允许时间间隔)做一些不同的事情。


2
因此,即使现实的时间只是一种输入,但由于它不是作为输入提供的,而是在函数的控制范围之外(在函数内部,在调用者的控制范围之外Today())也Today()变得不纯。该Today()功能可能是一个愚蠢的例子。一些Count()功能可能更合适。给定相同数量的项目Count()将始终返回相同的数字,但是由于超出了此范围,Count()因此是不正确的。
2013年

1
@brad有点灰色区域-有一个隐式实际参数-数组或列表。给定一个不变的列表和每次相同的参数,它将始终返回相同的值。
2013年

34
“现实的时间是一种投入” –是的;实际上,全局状态对于所有函数都是隐式可用的(即“某种输入”),但是如果它们的结果依赖于它,那么它们就是不纯的!
AakashM 2013年

4
count()大多数编程语言上的@Brad 绝对是纯净的。它有一个明确的输入值:想要计数的集合。不要被诸如myCollection.count();之类的语法所迷惑。那只是糖count(myCollection)
Andres F.

一如既往的好答案,但并未明确涵盖不可变的自由变量。它们不是函数的输入-不能作为参数传递-但是函数仍然依赖于它们,即使它仍然是参照透明的。

24

sin(x)只要x保持不变,将始终返回相同的值。Today()随着时间的流逝,它可能会返回不同的结果,因为它取决于您控制范围之外的值。例如,如果程序无法控制,$current_datetime 在程序运行更改了系统内部,Today()则会突然产生不同的结果。


“将始终返回不同的值”有点... 不正确的措词。维基百科说这意味着在周一获得的值不会相差“一周的当天返回”
蚊蚋

7
@gnat:的确如此,除非程序外部的某些内容更改了计算机的内部日历,使其突然认为是星期四。然后致电Today()将在星期一返回“星期四”。
FrustratedWithFormsDesigner 2013年

3
@gnat好吧,它不会总是返回不同的值(几乎没有任何有用的函数可以这样做)。但是,像大多数不纯函数一样,即使在执行单个程序期间(例如,如果它隔夜运行),返回值也可能会有所不同。

3
@delnan:是的,这就是天真的数据库脚本作者的祸根!:P“但是它怎么会错过300条记录呢?当我昨天早上测试它时,脚本运行良好!”
FrustratedWithFormsDesigner 2013年

@delnan可以肯定的。我只是指出,始终在初始用语中使用“ always ”(在当前版本中对“ can”的回答已得到纠正)有点不准确
2013年

13

Today()是一个不纯函数,因为它的结果取决于您不给它的东西;具体来说,当前系统时间。因此,当仅基于调用时提供的输入时,其结果是不确定的。

一个纯函数将是int Add(int a, int b) {return a + b;}。该函数仅使用给出的函数,不使用其他外部状态数据。这样的自然结果是,您可以Add(2,2)从现在到时间结束并获得4。另外,由于该函数不会更改任何外部状态(它没有“副作用”),所以从现在到时间结束,Add()ing 2和2不会更改系统中的其他任何内容,除非您随后将函数的结果分配给变量,或者使用该值更新状态(这不是函数本身执行的操作)。实际上,所有经典数学运算都是纯函数,因此可以实现。

另一方面,Today()连续两次调用时可能会产生相同的值,但是如果连续几天调用则不会产生相同的值。这是因为它依赖于外部状态数据,而外部状态数据不是您作为函数的参数提供的。结果,在程序范围内无法控制Today()函数的结果。它将在给定的日期产生给定的值,并且在任何一天都不会产生该值,除非您更改运行它的计算机的系统时钟(通常发生在程序范围之外的更改)。

函数不纯不一定是一件坏事。即使使用功能语言,也需要不纯函数才能与程序边界之外的任何事物进行交互,例如数据存储,通信管道,UI显示,外围设备等。不执行任何这些操作的程序就是程序其实用性受到极大限制;我什至可以称其为琐碎的程序,因为如果没有任何手段来接受输入或以任何方式将其输出告知您,它可能什么也没做。用功能语言编写的程序只能具有运行时提供的输入,并且可以向运行时报告输出,而无需任何明确定义的不正确方法,但这是因为运行时将所有在不完美的计算机系统中工作的所有不正确的细节抽象化了,

知道您正在使用的哪些函数是纯函数而哪些不是纯函数,这是一件非常好的事情,这样您就可以对如何使用它们做出正确的决定。不纯函数,因为它们会执行某些操作或依赖于从其用法中不明显的事物,所以仅在了解用法后,它们的行为可能无法预测。为了使使用该功能的系统处于一致状态,从而期望获得确定性的结果,需要进一步了解该功能的用途,以及从外部状态执行或需要向外部状态执行的操作。


8

显然,该函数未能通过该页面开头给出的首次纯度测试:

  1. 给定相同的参数值,该函数始终求值相同的结果值。函数结果值不能依赖于任何隐藏的信息或状态,这些信息或状态可能随着程序执行的进行或在程序的不同执行之间发生变化,也不能依赖于I / O设备的任何外部输入。

请注意,由于它不接受任何参数,因此只有一组可能的参数值-空集。对于相同的 “参数值” ,此函数可以并且确实返回不同的结果。

此外,功能结果值的确取决于“隐藏的...状态,该状态可能随着程序执行的进行而改变”。所以又失败了。


@JörgWMittag我不确定在哪里断言没有参数的函数不能返回值。
AakashM 2013年

脑放屁。我读到“只有一组可能的返回值”。
约尔格W¯¯米塔格

8

() => 1将是一个纯函数,因为它始终返回1。Today()可能返回“ Monday”或“ Tuesday”或几乎任何其他值。

另一种思考方式是纯函数不依赖状态。世界通常被认为是国家。您需要知道现实的状态才能知道今天是星期几。

但是,您无需了解世界状况的任何特殊信息即可知道它是什么sin(x)。调用给sin(x)x的值将返回相同的值。


维基百科说“返回星期几”,这意味着它可以返回星期一,星期二等,但不能返回“ 1/23/2013”​​或“ 1/24/2013”
咬2013年

7
@gnat:更新了,但是区别并不大。
Guvante

2

Date(timestamp)将是一个纯函数。由于其幂等性。而且因为不会有副作用。

Today()可以根据改变其结果是,当你调用它。这就是使它不纯净的原因。它不是幂等的。虽然它没有副作用,但是并不能使其纯净。


2

这是我在讨论纯函数时想到的一些伪代码

newValue = Function();
while(true)
{
   oldValue = newValue;
   newValue = Function();
   assert( newValue == oldValue );
}

如果它无限期地运行并且不能触发断言,则它是一个纯函数。更重要的是,如果您有使用args的函数,则需要进行一些修改。

oldValue = Function( importantVariableToYourApp );
newValue = Function( importantVariableToYourApp );
assert( newValue == oldValue );

如果您可以在应用程序中的每个变量分配之后使用它,并且它不会改变应用程序中的结果,并且它永远不会使断言失败,那么它就是一个纯函数。


2

首先,不存在没有参数的函数(或没有索引的数组或没有键的映射)。将一个或多个参数值映射到另一个值是函数的定义特征。

因此,today根本不是一个函数,因此也不是纯函数。否则我们可能会解释语法

today()

有点意思

today   ()      -- today, applied to the value ()

例如,在Haskell中,这将是有效的:

data Day = Mon | Tue | Wed | Thu | Fri | Sat | Sun deriving Show
today :: () -> Day
today () = ....?
main = print (today())

因为存在一个具有单个值()的类型()。

问题只是,today如果只有(),如何计算星期几?直接或通过助手不纯函数读取系统计时器是不可能的。

系统计时器是全局状态的一个很好的例子。


1

问题today()在于,如果在函数中调用两次或更多次,它将产生不同的结果。

这是一个代码示例,可能会引入一个错误。

function doSomething(when)
{
     if(today() == when)
     {
           // open a resource or create a temp file.....
     }

     // do some other work

     if(today() == when)
     {
           // close the resource or delete temp file.....
     }
}

在上面的示例中是可能的。第二条if语句将不会执行。即使第一个做到了。使资源处于不良状态。


1

要成为纯函数,每次提供相同的参数必须给出相同的结果。

每次调用时Today(),我们都会为其提供相同的参数(无),但不一定获得相同的结果(星期一,星期二等)。


4
这似乎只是重复了大约两年前发布的最佳答案中提出和解释的观点。几乎不值得碰撞两岁的问题有这样的内容
蚊蚋

1
我对stackexchange的工作方式不太熟悉,但我认为,由于这是头等大事,因此已经被碰到了。就重复点而言,我记得在阅读meta时,拥有多个相似的答案可能会有所帮助。我觉得我的观点很简洁,可能会有所帮助。
Zantier
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.