首页 > 代码库 > [Erl_Question02] 那些经历过的Erlang小坑(持续更新)

[Erl_Question02] 那些经历过的Erlang小坑(持续更新)

1. 保护式(guard)中如果出错,不会报错,只会返回false!

case 1=:1 of
     true when not erlang:length(t) =:= 1 orelse true ->
           ok;
     _ ->
         error
end.
Result is:   error

保护式中对t (atom) 求length会出错,本应crash掉,但因为在保护式中,默认出错后结束此保护式计算并返回false,这也是为什么保护式不接受复杂的函数,只能用erlang的bif来做的原因之一。

2. try catch 时如果不写catch类型,默认为throw类型!

try_catch(Value) ->
    try
        case Value  of
            error -> erlang:error({error,plz_catch_me});
            throw -> erlang:throw({throw,oh_get_you});
            exit -> erlang:exit({exit,sorry_by_mistake})
        end
    catch
        T -> T
    end.

Result :

try_catch

所以最好是:明确: Catch   throw:T –> {throw,T}; error:T –> {error,T}; exit:T –> {exit,T} end.

 

3. 在保护式中使用erlang:length/1要小心再小心!(要遍历列表,时间长度不定)

%%写成这样的耗时与列表长度成平方比:Do not do this
foo(List) when lenght(List) >0 ->
         do_something;
foo(_) ->
       done.
%%使用匹配模式来做可做到任意长度断定
better([One]) ->
       do_something_one();
better([One,Two]) ->
       do_something_two();
better([one,Two|_]) ->
       do_something_big();
better([]) ->
      do_something_empty()
end.

Tip:  如果要判定List是一个非空List 可用 case List of [_|_] –> do_something(); _ –> done end.

4. ++只是lists:append/2的一个别名:如果要用一定要确定 ShortList++LongList !(可记为长短的反义短长…每次用他我都会条件反射想一下)

%% DO NOT DO
naive_reverse([H|T]) ->
    naive_reverse(T)++[H];
naive_reverse([]) ->
    [].

which is the most inefficient way there is to reverse a list. Since the ++ operator copies its left operand, the result will be copied again and again and again... leading to quadratic complexity.

这是最没效率去反转一个list,”++“会复制左边的元素,这个会使复制多次,导致平方倍的复杂度。

但是另一方面:下面这种用法就好了:

 
%% OK
naive_but_ok_reverse([H|T], Acc) ->
    naive_but_ok_reverse(T, [H]++Acc);
naive_but_ok_reverse([], Acc) ->
    Acc.

这并不是一个是非常坏的尝试,每个列表元素只被copy一次,那增长的Acc是在++的右边的,他不会每次都被copy的

当然,最佳实践还是下面这种:

%% Best Do
vanilla_reverse([H|T], Acc) ->
    vanilla_reverse(T, [H|Acc]);
vanilla_reverse([], Acc) ->
    Acc.

这比上面的还要高效一点点,你根本不用去建造一个list元素,直接copy他就可以了(或者:如果编译器不把[H]++Acc重写为[H|Acc] ,那就更高效啦)。

5. receive 和case的区别很大,虽然写法类似:

case_test(Value) ->
    case Value of
        1 -> ok;
        2 -> error
    end.
receive_test(Value)when Value>2 ->
    PID = spawn(fun () ->
        receive
            {msg,1} ->
                ok;
            {msg,2} ->
                error
        end
    end),
    [begin PID ! {msg,ValueT} end ||ValueT<-lists:seq(3,Value)],
    PID.

Result:

recieve

从上面可以看出:

5.1 case如果没有匹配就会出错;

5.1 recieve 会在没有匹配消息时阻塞,只要信箱中没有匹配消息,就会在等待期间挂起,=有新消息到时才会被唤醒,每次执行时,receive会先检查最老的消息(位于队列头部),像在case表达式中那样尝试匹配,如果找不到,就会继续下一消息,如果与当前匹配成功,且保护式成立(如果有),此消息就会被移出信箱,同时子句对应的正文会被执行,如果一直没找到合适消息就会一直等待至超时(如果有的话,after Times).