[Erlang02] 那些经历过的Erlang小坑1-10

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

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来作的缘由之一。node

2. try catch 时若是不写catch类型,默认为throw类型!shell

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 :cookie

try_catch

因此最好是:明确: Catch   throw:T –> {throw,T}; error:T –> {error,T}; exit:T –> {exit,T} end.app

 

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.oop

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

%% 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.atom

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

可是另外一方面:下面这种用法就行了:

 
%% 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).

6. erl 用-noshell –noinput 启动一个node时,看不到,又不能输入怎么调试?用-remsh参数

>erl -name foo@127.0.0.1 -setcookie 123456 -noshell -noinput

>erl -name bob@127.0.0.1 -setcookie  123456 -remsh foo@127.0.0.1

%%注意起的节点叫foo哦,不是bob了!
foo@127.0.0.1> nodes().
foo@127.0.0.1>['bob@127.0.0.1']
foo@127.0.0.1>node().
foo@127.0.0.1>'foo@127.0.0.1'

这里的坑在于:

6.1 在remote出来的节点上调用q(),2个节点都会退出!好可怕,全部最好把q()这个函数在user_default.erl里面重写,让他不要执行init: stop().

6.2 window下要用werl 代替erl;

6.3 erl支持自定义参数,好比你写erl –rmsh test 也是不会报错的,若是不当心写错了,就会查很久……..

Tip: 已 起A,B二个节点,要用A 控制B,能够在A中使用Ctrl+G  r  NodeA j  2操做  具体见:Learn some erlang remote shell.

 

7.若是有一个ArgList 是:从不可知道的地方传过来是这样子:”[test1,test2,test3]”,要怎么使用才能动态执行?

场景:知道方法调用的方法:Method 使用 erlang:apply(Module,Method,ArgList)调用产生结果,这时的ArgList是不符合格式:

%% String = "[test1,test2,4]."注意最后面的结束小句号!
string_to_args(String) ->
    {ok, Scan1, _} = erl_scan:string(String),
    {ok,P}=erl_parse:parse_exprs(Scan1),
    {value,Value,[]} = erl_eval:exprs(P, []),
    Value.

以上合适List中全部的参数都是绑定的:,若是是有Test1这样的变量,我也没试过,还没遇到这种需求悲伤

能够参考

 

8. erl 启动参数能够本身定义:如

>erl -test erlang1  -test hello -test1 test1

>init:get_argument(test).
{ok,[["erlang1"],["hello"]]

>init:get_arguments().
[{root,["C:\\Program Files\\erl6.0"]},
 {progname,["erl"]},
 {home,["C:\\Users\\**"]},
 {test,["erlang1"]},
 {test,["hello"]}]

8.1  不要把参数定义为string了:好比 “hello world”

8.2 若是这个是启动application启动用的,就不要期望用这个自定义的参数了,用config定义吧

Applications should not normally be configured with command line flags, but should use the application environment instead. Refer to Configuring an Application in the Design Principles chapter for details

 

9.使用RPC时必定要记得你是在distributed的,时刻关注你在那个进程!

好比:把rpc:call放在loop里面和外面会获得不同的效率反馈,如下这例子的结果是等价的,可是第一种会发出不少的call,第二种只有一个call.

 
%%Example - Bad
[rpc:call(node, ets, lookup, [table, K]) || K <- ListOfKeys].

%%Example - Good
rpc:call(node, ?MODULE, get_data, [ListOfKeys, []]).
get_data([], Out) -> lists:reverse(Out);
get_data([K|ListOfKeys], Out) -> get_data(ListOfKeys, [ets:lookup(table,K)|Out]).

同理你能够本身改一下:[gen_server:call(Pid,{func,Fun})||Fun<- FunList].

总之要能一次发消息处理的就不要屡次发啦.
10 不要构造超极大的terms(或者你不可控制大小的terms). 
具体就是若是要遍历ets里面全部的元素,用ets:tab2list/1得出来的结果可能什么很是大,这可怎么办啊!
%% 一次性也出全部元素:不要这样子作
bad_traverse_to_print() ->
    [begin print(Person) end||Person <- ets:tab2list(person)],
    ok.

%%从第一个一直遍历到最后一个:数据要从erts内部搬到process 当ets很大的时候就效率低
good_traverse_to_print() ->
    good_traverse_to_print2(ets:first(person)).

good_traverse_to_print2('$end_of_table') ->
    ok;
good_traverse_to_print2(Key) ->
    [Person] = ets:lookup(person,Key),
    print(Person),
    good_traverse_to_print2(ets:next(person,Key)).

%%分页:最佳实践使用ets:select match MatchSpec:ets内部实现了把matchspec编译成opcode 而后eval的时候把须要的数据才拷贝到process去 大大减小了数据量
best_traverse_to_print() ->
    case ets:match(person,'$1',10) of
        {PersonList,'$end_of_table'} ->
            [begin print(Person) end|| [Person] <- PersonList];
        {PersonList,Key} ->
            [begin print(Person) end|| [Person] <- PersonList],
            best_traverse_to_print2(Key)
    end,
    ok.
best_traverse_to_print2(Key) ->
    case ets:match(Key) of
        {PersonList,'$end_of_table'} ->
            [begin print(Person) end|| [Person] <- PersonList];
        {PersonList,Key2} ->
            [begin print(Person) end|| [Person] <- PersonList],
            best_traverse_to_print2(Key2)
    end.

print(Person) ->
    io:format("Name~p     Phone:~p~n",[Person#person.name, Person#person.phone]),
    ok.
第10条和第9条是看似矛盾的,一个说若是能够一次处理完就不要分屡次,一个是若是太大就要分屡次!注意,若是一个消息体太大了,也要分屡次哦。
相关文章
相关标签/搜索