OCaml:存储一些以后要使用的值会引入“副作用”吗?

时间:2011-09-28 05:34:22

标签: functional-programming ocaml side-effects

对于家庭作业,我们已被指示完成任务而不引入任何“副作用”。我在维基百科上查找了“副作用”,虽然我理论上它意味着“修改一个状态或者与调用函数有一个可观察的交互”,但我很难搞清楚具体细节。

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

说我有(可能在语法上不完美):

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

这是否会引入副作用?我想也许我对“修改状态”在副作用的定义中意味着什么感到困惑。

2 个答案:

答案 0 :(得分:8)

没有;副作用是指例如使用赋值运算符ref变换:=单元格,或者名称引用的值随时间变化的其他内容。在这种情况下,myList是一个永不变量的值,在程序中永远不会改变,因此它是无效的。

另见

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

答案 1 :(得分:5)

考虑它的一个好方法是“我有没有改变任何后来的代码(包括以后再次运行同一个函数)可能会看到除了我返回的值之外的其他代码?”如果是这样,那就是副作用。如果没有,那么你可以知道没有一个。

所以,比如:

let inc_nosf v = v+1

没有副作用,因为它只返回一个比整数v多一个的新值。所以如果你在ocaml toplevel中运行以下代码,你会得到相应的结果:

# 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等函数修改字符串。基本上,任何可以修改数据类型的函数都会产生副作用。