it-swarm.cn

什么是“副作用”?

我还不清楚副作用的概念。

  • 编程的副作用是什么?
  • 是否依赖于编程语言?
  • 是否存在外部和内部副作用?

请举例说明产生副作用的原因。

101
Amir Rezaei

副作用 仅指对某种状态的修改-例如:

  • 更改变量的值;
  • 将一些数据写入磁盘;
  • 在用户界面中启用或禁用按钮。

Contrary似乎有人在说:

  • 副作用确实not必须隐藏或意外(可以,但与定义无关,因为它适用于计算机科学);

  • 副作用与幂等性有关nothing。幂等函数可能有副作用,而非幂等函数可能没有副作用(例如获取当前系统日期和时间)。

这真的非常简单。副作用=在某处更改某些内容。

附言正如评论者benjol指出的那样,可能有人将副作用的定义与 纯函数 的定义混淆了,该函数是(a)幂等的,而(b)没有副作用-效果。在一般计算机科学中,一种并不暗示另一种,但是功能编程语言通常倾向于强制执行这两种约束。

114
Aaronaught

修改计算机状态或与外界交互的任何操作都被认为具有副作用。请参阅Wikipedia上的 副作用

例如,此功能没有副作用。其结果仅取决于其输入参数,而在调用该程序时,其状态或环境不会改变:

int square(int x) { return x * x; }

相反,根据调用它们的顺序,调用这些函数将给您不同的结果,因为它们会改变计算机的状态:

int n = 0;
int next_n() { return n++; }
void set_n(int newN) { n = newN; }      

此功能具有将数据写入输出的副作用。您不调用该函数是因为您需要它的返回值。您之所以称其为是因为您希望它对“外部世界”具有影响:

int Write(const char* s) { return printf("Output: %s\n", s); }
38
Kristopher Johnson

我认为现有的答案是相当不错的。我想在某些方面阐述海事组织的压力不够大。

在数学中,函数只是从值的元组到值的映射。因此,给定一个函数f和一个值xf(x)将始终是相同的结果y。您可以在表达式中的任何地方都用y替换f(x),并且什么都不会改变。

在许多编程语言中,所谓的函数(或过程)是可以执行的构造(代码段),因为:

  1. 它从数学意义上计算一个函数,即给定输入值,它返回结果,或者
  2. 它会产生一些效果,例如在屏幕上打印内容,更改数据库中的值,发射导弹,睡眠10秒钟,发送短信。

因此效果可能与状态有关,也可能与其他方面有关,例如发射导弹或暂停执行几秒钟。

术语“副作用”听起来可能是负面的,但通常情况下,调用函数的作用是函数本身的目的。我猜想,由于函数一词最初是在数学中使用的,因此计算值被认为是函数的主要作用,而其他任何作用都被认为是副作用 。某些编程语言使用术语过程以避免与数学意义上的函数混淆。

注意

  1. 一些过程对于它们的返回值和副作用都是有用的。
  2. 某些过程仅计算结果值,而没有其他影响。它们之所以称为纯函数,是因为它们所做的只是在数学意义上计算一个函数。
  3. 一些程序,例如Python中的sleep()仅对它们的(副作用)有用。这些通常被建模为返回特殊值Noneunit()或...,仅表示计算已正确终止。
24
Giorgio

副作用是操作对超出预期用途的变量/对象产生影响时。

当您调用具有更改某些全局变量的副作用的复杂函数时,可能会发生这种情况,即使这不是您调用它的原因(也许您调用它是为了从数据库中提取某些内容)。

我承认我很难提出一个看起来并不完全人为的简单示例,而且我所研究的内容中的示例太久了,无法在此处发布(并且由于与工作相关,因此我可能无论如何都不应该这样做) )。

我之前看到的一个示例是一个函数,如果该连接处于关闭状态,则该函数将打开数据库连接。问题在于应该在函数末尾关闭连接,但是开发人员忘记添加该代码。因此,这里有一个意想不到的副作用:调用过程只应该执行查询,并且副作用是连接保持打开状态,并且如果该函数连续两次被调用,将引发错误消息,表明连接已打开。


好的,既然现在每个人都在举一些例子,我想我也会;)

/*code is PL/SQL-styled pseudo-code because that's what's on my mind right now*/

g_some_global int := 0; --define a globally accessible variable somewhere.

function do_task_x(in_a in number) is
begin
    b := calculate_magic(in_a);
    if b mod 2 == 0 then
        g_some_global := g_some_global + b;
    end if;
    return (b * 2.3);
end;

功能 do_task_x具有primary返回某些计算结果的效果,以及side可能修改全局变量的效果。

当然,哪个是主要的,副作用是可以接受解释的,可能取决于实际用法。如果我出于修改全局变量的目的调用此函数,并且放弃返回的值,那么我会说修改全局变量是主要作用。

4

在计算机科学中,如果函数或表达式修改某些状态或与调用函数或外界有可观察的交互作用,则称其具有副作用。

来自 维基百科-副作用

从数学意义上讲,函数是从输入到输出的映射。调用函数的预期效果是使函数将输入映射到返回的输出。如果该函数执行其他任何操作,则无关紧要,但是,如果该函数具有未将输入映射到输出的任何行为,则该行为是已知的副作用。

更一般地讲,副作用是不是构造设计者的预期效果的任何效果。

效果是影响演员的任何事物。如果我调用一个向女友发送分手短信的功能,则该功能会影响一堆演员,包括我,她,手机公司的网络等。调用无副作用功能的唯一预期效果是该功能从我的输入返回一个映射。因此对于:

   public void SendBreakupTextMessage() {
        Messaging.send("I'm breaking up with you!")
   }

如果打算将其用作函数,则它唯一要做的就是返回void。如果没有副作用,则它实际上不应发送短信。

在大多数编程语言中,没有数学函数的构造。没有打算照此使用任何构造。这就是为什么大多数语言都说您有方法或过程的原因。通过设计,它们旨在能够产生更多的效果。用普通的编程术语来说,没有人真正在乎方法或过程的意图,因此,当有人说此函数有副作用时,他们实际上是在说,这种构造不会起作用就像一个数学函数。当有人说这个函数没有副作用时,他们的意思是说,这个构造实际上就像数学函数一样工作。

根据定义,纯功能始终没有副作用。纯粹的函数可以说是一种函数,即使它使用的结构允许更多的效果,但实际上仅具有与数学函数相同的效果。

我挑战任何人告诉我什么时候没有副作用的纯函数会变得不纯净。除非使用术语“纯”和“无副作用”的句子上下文的主要预期效果不是函数的数学预期效果,否则它们始终相等。

因此,有时(尽管很少),并且我认为这是公认的答案中缺乏区别并且误导人们的原因(因为这不是最常见的假设),但是有时假定编程功能的预期效果是将输入映射到输出,其中输入不限于函数的显式参数,而输出则限于显式返回值。如果您认为这是预期效果,那么由于允许输入来自预期效果中的其他位置,因此读取文件并根据文件中内容返回不同结果的函数仍然没有副作用。

那么,为什么这一切都很重要?

这一切都与控制和保持控制有关。如果调用一个函数,然后又执行其他操作然后返回一个值,则很难推断出它的行为。您将需要在函数内部查找实际代码,以猜测其作用并断言其正确性。理想的情况是,很清楚并且很容易知道函数正在使用什么输入,并且它没有做任何其他事情然后为它返回输出。您可以对此稍作放松,并说确切地知道它正在使用什么输入并不像确定它没有做任何您可能不知道的然后返回值的事情那样有用,所以也许您对仅执行感到满意它不执行任何其他操作,然后将输入(无论从何处获取)映射到输出。

在几乎所有情况下,程序的目的都是要产生影响,然后再将输入的内容映射到输出的内容。控制副作用的想法是,您可以以一种易于理解和推理的方式来组织代码。如果将所有副作用汇总在一起,放在一个非常明确和关键的地方,就很容易知道在哪里看,并相信这就是正在发生的一切,仅此而已。如果您的输入也非常明确,则可以帮助测试不同输入的行为,并且使用起来更容易,因为您无需在很多不同的地方更改输入,只是其中一些可能并不明显。得到你想要的。

因为最有助于理解,推理和控制程序行为的地方是将所有输入清楚地分组在一起并且是明确的,并且将所有副作用都分组在一起并且是明确的,所以通常这就是人们所说的副作用,纯等.

因为最有用的是对副作用及其明确性进行分组,所以有时人们只会表示此意思,并通过说它不是纯净的,而是仍然是“副作用”自由的来区分它。但是副作用是相对于假定的“预期的主要作用”的,因此它是一个上下文术语。我发现这种用法很少使用,尽管令人惊讶的是在该线程中讨论了很多。

最后,幂等意味着用相同的输入多次调用此函数(无论它们来自何处)始终会产生相同的效果(是否有副作用)。

3
Didier A.

在编程中,副作用是过程从其范围之外更改变量时。副作用与语言无关。有一些旨在消除副作用的语言(纯功能语言),但是我不确定是否有任何需要副作用的语言,但是我可能是错的。

据我所知,没有内部和外部的副作用。

2
indyK1ng

这是一个简单的示例:

int _totalWrites;
void Write(string message)
{
    // Invoking this function has the side effect of 
    // incrementing the value of _totalWrites.
    _totalWrites++;
    Debug.Write(message);
}

副作用的定义并非特定于编程,因此只需想象一下药物或进食过多的副作用即可。

0
ChaosPandion