幂等函数和确定性函数有什么区别?

What is the difference between an Idempotent and a Deterministic function?

它们(幂等函数和确定性函数)是否只是在给定相同输入的情况下 return 产生相同结果的函数?

或者我遗漏了什么区别? (如果有区别,请你帮我理解它是什么)

确定性函数只是数学意义上的函数。给定相同的输入,您总是会得到相同的输出。另一方面,幂等函数是满足恒等式

的函数
f(f(x)) = f(x) 

举个简单的例子。如果UCase()是一个将字符串转换为大写字符串的函数,那么显然UCase(Ucase(s)) = UCase(s).

幂等函数是所有函数的子集。

更简单地说:

  • 纯确定性函数:输出完全且仅基于输入值,仅此而已:没有其他(隐藏的)输入或它所依赖的状态来生成它的输出。没有副作用或其他输出。
  • 不纯确定性函数:与作为纯函数的确定性函数一样:输出完全且仅基于输入值,仅此而已:没有它依赖于生成其输出的其他(隐藏)输入或状态 - 但是还有其他输出(副作用)。
  • 幂等性:实用的定义是你可以安全地多次调用同一个函数而不用担心负面影响。更正式地说:在后续的相同调用之间没有状态变化

幂等性并不意味着确定性(因为一个函数可以在第一次调用时改变状态,而在后续调用中是幂等的),但所有纯确定性函数本质上都是幂等的(因为没有内部状态在调用之间保持不变)。不纯的确定性函数不一定是幂等的。

Pure deterministic Impure deterministic Pure Nondeterministic Impure Nondeterministic Idempotent
Input Only parameter arguments (incl. this) Only parameter arguments (incl. this) Parameter arguments and hidden state Parameter arguments and hidden state Any
Output Only return value Return value or side-effects Only return value Return value or side-effects Any
Side-effects None Yes None Yes After 1st call: Maybe.
After 2nd call: None
SQL Example UCASE CREATE TABLE GETDATE DROP TABLE
C# Example String.IndexOf DateTime.Now Directory.Create(String)Footnote1
  • 脚注 1 - Directory.Create(String) 是幂等的,因为如果目录已经存在,它不会引发错误,而是 return 一个新的 DirectoryInfo 实例指向指定的现有文件系统目录(而不是先 创建 文件系统目录,然后 return 创建一个指向它的新 DirectoryInfo 实例)——这就像 Win32 的方式一样CreateFile 可用于 打开 现有文件。

关于非标量参数this和变异输入参数的临时说明:

(我目前不确定 OOP 语言中的实例方法(带有隐藏的 this 参数)如何归类为 pure/impure 或确定性或非确定性 - 尤其是 当谈到改变 this 的目标时 - so I've asked the experts in CS.SE to help me come to an answer - 一旦我得到满意的答案,我会更新这个答案)。

关于例外的说明

今天许多(大多数?)编程语言将抛出的异常视为 return 的单独“种类”(即“return 到最近的 catch”)或作为显式副作用(通常是由于该语言运行时的工作方式)。但是,就此答案而言,给定函数抛出异常的能力不会改变其 pure/impure/确定性/非确定性标签 - 同上幂等性(实际上: 抛出通常是首先实现幂等性的方式 例如,一个函数可以通过在它进行这些状态更改之前立即抛出来避免引起任何副作用 - 但也可以简单地 return .).

因此,对于我们的 CS 理论 目的,如果给定函数 可以 抛出异常,那么您可以将异常简单地视为该函数输出的一部分。 重要的是 是否确定性地抛出异常,并且如果(例如 List<T>.get(int index) 确定性地抛出 if index < 0 ).

注意事项are very different for functions that catch exceptions,不过

纯函数的确定性

例如,在 SQL UCASE(val) 或 C#/.NET 中 String.IndexOf 都是确定性的,因为输出仅取决于输入。请注意,在实例方法(例如 IndexOf)中,实例对象(即隐藏的 this 参数)算作输入,即使它是“隐藏的”:

"foo".IndexOf("o") == 1 // first cal
"foo".IndexOf("o") == 1 // second call
// the third call will also be == 1

而在 SQL NOW() 或 C#/.NET 中 DateTime.UtcNow 不是确定性的,因为即使输入保持不变,输出也会改变(请注意 属性 .NET 中的 getter 等价于除了隐式 this 参数之外不接受任何参数的方法:

 DateTime.UtcNow == 2016-10-27 18:10:01 // first call
 DateTime.UtcNow == 2016-10-27 18:10:02 // second call

幂等性

.NET 中的一个很好的例子是 Dispose() 方法:参见 Should IDisposable.Dispose() implementations be idempotent?

a Dispose method should be callable multiple times without throwing an exception.

因此,如果父组件 Xfoo.Dispose() 进行初始调用,那么它将调用处置操作,并且 X 现在可以考虑处置 foo。 Execution/control 然后传递给另一个组件 Y,然后它也尝试处理 foo,在 Y 调用 foo.Dispose() 之后它也可以期望 foo 到被处置(它是),即使 X 已经处置了它。这意味着 Y 不需要检查 foo 是否已经被处置,从而节省了开发人员的时间 - 并且还消除了第二次调用 Dispose 可能引发异常的错误,例如.

REST 中的另一个(一般)示例:HTTP1.1 的 RFC 声明 GETHEADPUTDELETE 是幂等的,但是POST 不是 ( https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html )

Methods can also have the property of "idempotence" in that (aside from error or expiration issues) the side-effects of N > 0 identical requests is the same as for a single request. The methods GET, HEAD, PUT and DELETE share this property. Also, the methods OPTIONS and TRACE SHOULD NOT have side effects, and so are inherently idempotent.

所以如果你使用 DELETE 那么:

Client->Server: DELETE /foo/bar
// `foo/bar` is now deleted
Server->Client: 200 OK
Client->Server DELETE /foo/bar
// foo/bar` is already deleted, so there's nothing to do, but inform the client that foo/bar doesn't exist
Server->Client: 404 Not Found
// the client asks again:
Client->Server: DELETE /foo/bar
// foo/bar` is already deleted, so there's nothing to do, but inform the client that foo/bar doesn't exist
Server->Client: 404 Not Found

所以你在上面的例子中看到 DELETE 是幂等的,因为服务器的状态在最后两个 DELETE 请求之间没有改变,但它不是确定性的,因为服务器 return为第一个请求编辑 200,但为第二个请求编辑 404

一个确定性函数将return相同输入的相同结果,无论你调用它多少次。

idempotent 函数可能不会 return 相同的结果(它将 return 结果以相同的形式但值可能不同,请参阅下面的 http 示例)。它只保证它不会有副作用。换句话说,它不会改变任何东西。

例如,GET 动词在 HTTP 协议中意味着是幂等的。如果您调用“~/employees/1”,它将以特定格式 return ID 为 1 的员工的信息。除了 return 员工信息外,它永远不会改变任何东西。如果调用它 10 次、100 次左右,returned 格式将始终相同。但是,它绝不是确定性的。也许如果你第二次调用它,员工信息已经改变或者员工甚至不再存在。但它绝不应该有副作用或 return 不同格式的结果。

我的意见

幂等是一个奇怪的词,但知道来源会很有帮助,idem 意思是 same and potent意思是power。换句话说,它意味着 具有相同的力量 这显然并不意味着没有 副作用 所以不确定这是从哪里来的。一个经典的例子 计算机科学中只有两件难事,缓存失效和命名。 为什么他们不能直接使用 read-only?哦,等等,也许他们想让自己听起来更聪明?也许像 圈复杂度?