8

对于家庭作业,我们被指示在不引入任何“副作用”的情况下完成任务。我在 Wikipedia 上查找了“副作用”,虽然理论上我明白这意味着“修改状态或与调用函数有可观察的交互”,但我无法弄清楚具体细节。

例如,创建一个包含非编译时结果的值会引入副作用吗?

假设我有(可能在语法上并不完美):

val myList = (someFunction x y);;
if List.exists ((=) 7) myList then true else false;;

这会带来副作用吗?我想也许我对副作用定义中的“修改状态”意味着什么感到困惑。

4

2 回答 2

8

不; 副作用是指例如ref使用赋值运算符改变单元格:=,或名称所引用的值随时间变化的其他事情。在这种情况下,myList是一个在程序期间永远不会改变的不可变值,因此它是无影响的。

也可以看看

http://en.wikipedia.org/wiki/Referential_transparency_(computer_science)

于 2011-09-28T05:40:09.887 回答
5

思考它的一个好方法是“我是否更改了任何以后的代码(包括稍后再次运行相同的函数)可能看到的任何东西,而不是我返回的值?” 如果是这样,那就是副作用。如果没有,那么您可以知道没有。

所以,像:

let inc_nosf v = v+1

没有副作用,因为它只返回一个比整数 v 大一的新值。因此,如果您在 ocaml 顶层运行以下代码,您会得到相应的结果:

# let x = 5;;
val x : int = 5
# inc_nosf x;;
- : int = 6
# x;;
- : int = 5

如您所见,x 的值没有改变。因此,由于我们没有保存返回值,所以没有真正增加任何内容。我们的函数本身只修改返回值,而不是 x 本身。所以要将它保存到 x 中,我们必须这样做:

# let x = inc_nosf x;;
val x : int = 6
# x;;
- : int = 6

由于 inc_nosf 函数没有副作用(也就是说,它只使用其返回值与外界通信,而不是通过进行任何其他更改)。

但是像:

let inc_sf r = r := !r+1

有副作用,因为它改变了存储在由 r 表示的引用中的值。所以如果你在顶层运行类似的代码,你会得到这个,而不是:

# let y = ref 5;;
val y : int ref = {contents = 5}
# inc_sf y;;
- : unit = ()
# y;;
- : int ref = {contents = 6}

因此,在这种情况下,即使我们仍然不保存返回值,它还是会递增。这意味着必须对返回值以外的其他内容进行更改。:=在这种情况下,该更改是使用它更改了 ref 的存储值的分配。

作为一个好的经验法则,在 Ocaml 中,如果您避免使用 refs、记录、类、字符串、数组和哈希表,那么您将避免任何副作用风险。尽管只要避免使用 String.set 或 String.fill 等函数就地修改字符串,您就可以安全地使用字符串文字。基本上,任何可以就地修改数据类型的函数都会产生副作用。

于 2011-09-28T06:03:39.440 回答