【发布时间】:2016-10-28 00:29:22
【问题描述】:
它们(幂等函数和确定性函数)是否都只是在给定相同输入的情况下返回相同结果的函数?
或者我缺少什么区别? (如果有区别,请您帮我理解它是什么)
【问题讨论】:
它们(幂等函数和确定性函数)是否都只是在给定相同输入的情况下返回相同结果的函数?
或者我缺少什么区别? (如果有区别,请您帮我理解它是什么)
【问题讨论】:
更简单的说法:
幂等性并不意味着确定性(因为函数可以在第一次调用时改变状态,而在后续调用中是幂等的),但所有纯确定性函数本质上都是幂等的(因为在调用之间没有保持内部状态)。不纯的确定性函数不一定是幂等的。
| 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
|
Directory.Create(String) 是幂等的,因为如果目录已经存在,它不会引发错误,而是返回一个新的 DirectoryInfo 实例,指向指定的现存文件系统目录(而不是 创建 首先是文件系统目录,然后返回一个指向它的新 DirectoryInfo 实例)——这就像 Win32 的 CreateFile 可用于打开现有文件。this 和可变输入 参数的临时说明:(我目前不确定 OOP 语言中的实例方法(带有隐藏的 this 参数)如何分类为纯/不纯或确定性 - 尤其是 this 的目标 - so I've asked the experts in CS.SE to help me come to an answer - 一旦我得到满意的答案,我会更新这个答案。
当今许多(大多数?)编程语言将抛出的异常视为单独的“种类”返回(即“return 到最近的catch”)或显式副作用 (通常是由于该语言的运行时工作方式)。但是,就这个答案而言,给定函数抛出异常的能力不会改变其纯/不纯/确定性/非确定性标签 - 同上幂等性(事实上:抛出通常是如何幂等性首先实现例如,一个函数可以通过在它进行这些状态更改之前简单地抛出来避免引起任何副作用 - 但或者它也可以简单地return。)。)。 p>
因此,出于我们CS 理论 的目的,如果给定函数可以 抛出异常,那么您可以将异常视为该函数输出的简单部分。 重要是异常是否被确定性地抛出,以及是否(例如List<T>.get(int index)确定性地抛出如果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 中的属性获取器等同于不接受任何参数的方法隐含的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?
Dispose 方法应该可以多次调用而不会引发异常。
因此,如果父组件X 对foo.Dispose() 进行初始调用,那么它将调用处置操作,X 现在可以考虑处置foo。执行/控制然后传递给另一个组件Y,它也尝试处理foo,在Y 调用foo.Dispose() 之后,它也可以期望foo 被处理(它是),即使@987654367 @ 已经处理了它。这意味着Y 不需要检查foo 是否已被释放,从而节省了开发人员的时间 - 并且还消除了第二次调用Dispose 可能引发异常的错误。
另一个(一般)示例在 REST 中:HTTP1.1 的 RFC 声明 GET、HEAD、PUT 和 DELETE 是幂等的,但 POST 不是(https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html)
方法还可以具有“幂等性”,因为(除了错误或过期问题)N > 0 个相同请求的副作用与单个请求相同。 GET、HEAD、PUT 和 DELETE 方法共享此属性。此外,方法 OPTIONS 和 TRACE 不应该有副作用,因此本质上是幂等的。
所以如果你使用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请求之间没有改变,但它不是确定性的,因为服务器返回了第一个200请求但 404 用于第二个请求。
【讨论】:
int f(x) { i = i+1; return 42; } 定义的程序中的函数f(即f 总是返回42,但递增一个全局变量a)是确定性的,但不是幂等的。也许这篇文章中“确定性”和“纯粹”混淆了。
deterministic 本身并不意味着idempotent 这一事实。如果您有一个基于相同输入生成相同输出但有副作用的函数怎么办? Idk 例如,它在其中执行另一个函数调用,改变其他地方的某些状态,或修改可变数据类型等(并不是说这将是一个好的设计,实际上它是一个糟糕的设计,它应该是一直避免),但它表明它不是idempotent。我会说你的定义是正确的,但后一种说法是不正确的。
CREATE TABLE IF NOT EXISTS 是不纯的非确定性和幂等的。我不熟悉CREATE OR REPLACE TABLE 的语义,但我认为即使架构相同,它也会始终删除/覆盖表?如果是这样,那么CREATE OR REPLACE TABLE 将是不纯的非确定性的,但仅在不切实际的学术纯数据库系统中是幂等的 - 实际上,此操作不是幂等的,因为它在数据库引擎的其他地方运行时每次都会改变状态(例如磁盘上的存储页面、统计信息、计数器/指标、审计日志等。
确定性函数只是数学意义上的函数。给定相同的输入,您总是得到相同的输出。另一方面,幂等函数是满足恒等式的函数
f(f(x)) = f(x)
作为一个简单的例子。如果UCase()是一个将字符串转换为大写字符串的函数,那么显然UCase(Ucase(s)) = UCase(s)。
幂等函数是所有函数的子集。
【讨论】:
f(x, y) 可能是合适的,所以在表已经存在的情况下,f(x, f(x, y)) = f(x, y) ...哦...是不同的还是我只是绕道到达同一个f(f(x)) = f(x) 的方法?
无论您调用多少次,确定性函数都会为相同的输入返回相同的结果。
idempotent 函数可能不会返回相同的结果(它会以相同的形式返回结果,但值可能不同,请参见下面的 http 示例)。它只保证它不会有副作用。换句话说,它不会改变任何东西。
例如,GET 动词在 HTTP 协议中是幂等的。如果您调用“~/employees/1”,它将以特定格式返回 ID 为 1 的员工的信息。它不应该改变任何东西,而只是简单地返回员工信息。如果您调用它 10、100 次左右,返回的格式将始终相同。但是,它绝不可能是确定性的。也许如果您第二次调用它,则员工信息已更改,或者该员工甚至不再存在。但绝不应该有副作用或以不同的格式返回结果。
我的意见
幂等是一个奇怪的词,但知道起源会很有帮助,idem 意思是 same 而 potent 意思是 power时间>。换句话说,它意味着具有相同的力量,这显然并不意味着没有副作用,所以不确定它来自哪里。 计算机科学中只有两个困难的事情,缓存失效和命名事情。他们为什么不能只使用只读?哦等等,他们想听起来更聪明,也许?或许像圈复杂度?
【讨论】: