对于家庭作业,我们已被指示完成任务而不引入任何“副作用”。我在维基百科上查找了“副作用”,虽然我理论上它意味着“修改一个状态或者与调用函数有一个可观察的交互”,但我很难搞清楚具体细节。
例如,创建一个保存非编译时结果的值会引入副作用吗?
说我有(可能在语法上不完美):
val myList = (someFunction x y);;
if List.exists ((=) 7) myList then true else false;;
这是否会引入副作用?我想也许我对“修改状态”在副作用的定义中意味着什么感到困惑。
答案 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等函数修改字符串。基本上,任何可以修改数据类型的函数都会产生副作用。