[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條是看似矛盾的,一個說若是能夠一次處理完就不要分屢次,一個是若是太大就要分屢次!注意,若是一個消息體太大了,也要分屢次哦。
相關文章
相關標籤/搜索