在Erlang中以end和ok结束函数有什么区别?我一直试图掌握以下代码中的含义:
-module(esOne).
-export([start/1, func/1]).
start(Par) ->
io:format("Client: I am ~p, spawned by the server: ~p~n",[self(),Par]),
spawn(esOne, func, [self()]),
Par ! {onPid, self()},
serverEsOne ! {onName, self()},
receiveMessage(),
ok.
receiveMessage() ->
receive
{reply, N} ->
io:format("Client: I received a message: ~p~n",[N])
after
5000->
io:format("Client: I received no message, i quit~n",[])
end.
func(Parent)->
io:format("Child: I am ~p, spawned from ~p~n",[self(),Parent]).
此代码与另一个充当服务器的.erl文件一起使用。我设法写这个只是通过分析给定的服务器文件并复制它的行为。首先我认为ok用于结束每个函数,但事实并非如此,因为我无法使用ok结束receiveMessage()。然后我想我可以用end结束每个函数,但是如果我用end替换ok,start(Par)将会出错。不仅如此,但在服务器文件中,我看到ok和end在函数内用于结束循环。他们使用的方式对我来说看起来是一样的,但他们显然完成了一个单独的功能,因为一个人不能被另一个人取代。一些澄清将非常感激。
答案 0 :(得分:4)
要理解两点:
Erlang中的某些代码块类型以" end"关闭。所以if ... end
,cond ... end
,receive ... [after N] ... end
等等。当然可以使用" end"作为自己的原子代替OK,但这不是上面发生的事情。
Erlang中的每个函数都返回一些值。如果您没有明确说明它,它将返回最后一个表达式的值。 " ="运算符不像其他语言那样赋值给变量,它在数学中赋值给符号,这意味着重新分配实际上是逻辑断言。如果断言失败,则进程抛出异常(通常意味着它崩溃)。
当你以" ok"结束某事时或者您提供已知最终值的任何其他原子将被返回。你不必对它做任何事情,但如果你想让调用过程声明函数已完成或崩溃,如果发生任何异常,那么你可以:
do_stuff() ->
ok = some_func().
而不是
do_stuff() ->
some_func().
如果some_func()可能有副作用可能会失败,它通常会返回ok
或{error, Reason}
(或类似的东西)。通过检查返回值是ok
,我们阻止调用进程在发生错误时继续执行。这是Erlang概念的核心,让它崩溃"。基本的想法是,如果你调用一个具有副作用的函数并且它会做任何意想不到的事情,你应该立即崩溃,因为继续处理坏数据比完全不进行更糟糕。崩溃将由主管清理,系统将恢复到已知状态,而不是在副作用失败后留下任何随机情况。
以上位的变化是" ok"如果函数的目的是返回一个值,则部分出现在元组中。例如,您可以在任何dict类型处理库中看到这一点。某些数据返回函数的返回类型为{ok, Value} | {error, Reason}
而非Value | {error, Reason}
的原因是为了使模式匹配更自然。
考虑以下案例条款:
case dict:find(Key, Dict) of
{ok, Value} ->
Value;
{error, Reason} ->
log(error, Reason),
error
end.
和
case finder(Key, Struct) of
Value ->
Value;
{error, Reason}
log(error, Reason),
error
end.
在第一个示例中,我们首先匹配成功条件。但是在第二个版本中,这是不可能的,因为错误子句永远不会匹配;任何回报都将始终由Value
表示。糟糕。
返回值或崩溃的大多数时间(但并不总是)函数将仅返回值。对于没有状态的纯函数尤其如此,但传入的内容并没有副作用(例如,dict:fetch/2
直接给出值,或者使调用进程崩溃,让您可以轻松选择所需的方式做事)。返回值或发出错误信号的函数通常会在{ok, Value}
中包含有效响应,因此很容易匹配。