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

  1. case 1=:1 of
  2. true when not erlang:length(t) =:= 1 orelse true ->
  3. ok;
  4. _ ->
  5. error
  6. end.

Result is:   error

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

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

  1. try_catch(Value) ->
  2. try
  3. case Value of
  4. error -> erlang:error({error,plz_catch_me});
  5. throw -> erlang:throw({throw,oh_get_you});
  6. exit -> erlang:exit({exit,sorry_by_mistake})
  7. end
  8. catch
  9. T -> T
  10. end.

Result :

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

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

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

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

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

  1. %% DO NOT DO
  2. naive_reverse([H|T]) ->
  3. naive_reverse(T)++[H];
  4. naive_reverse([]) ->
  5. [].

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,”++“会复制左边的元素,这个会使复制多次,导致平方倍的复杂度。

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

  1.  
  1. %% OK
  2. naive_but_ok_reverse([H|T], Acc) ->
  3. naive_but_ok_reverse(T, [H]++Acc);
  4. naive_but_ok_reverse([], Acc) ->
  5. Acc.

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

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

  1. %% Best Do
  2. vanilla_reverse([H|T], Acc) ->
  3. vanilla_reverse(T, [H|Acc]);
  4. vanilla_reverse([], Acc) ->
  5. Acc.

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

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

  1. case_test(Value) ->
  2. case Value of
  3. 1 -> ok;
  4. 2 -> error
  5. end.
  6. receive_test(Value)when Value>2 ->
  7. PID = spawn(fun () ->
  8. receive
  9. {msg,1} ->
  10. ok;
  11. {msg,2} ->
  12. error
  13. end
  14. end),
  15. [begin PID ! {msg,ValueT} end ||ValueT<-lists:seq(3,Value)],
  16. PID.

Result:

从上面可以看出:

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

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

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

  1. >erl -name foo@127.0.0.1 -setcookie 123456 -noshell -noinput
  2.  
  3. >erl -name bob@127.0.0.1 -setcookie 123456 -remsh foo@127.0.0.1
  4.  
  5. %%注意起的节点叫foo哦,不是bob了!
  6. foo@127.0.0.1> nodes().
  7. foo@127.0.0.1>['bob@127.0.0.1']
  8. foo@127.0.0.1>node().
  9. 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是不符合格式:

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

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

可以参考

8. erl 启动参数可以自己定义:如

  1. >erl -test erlang1 -test hello -test1 test1
  2.  
  3. >init:get_argument(test).
  4. {ok,[["erlang1"],["hello"]]
  5.  
  6. >init:get_arguments().
  7. [{root,["C:\\Program Files\\erl6.0"]},
  8. {progname,["erl"]},
  9. {home,["C:\\Users\\**"]},
  10. {test,["erlang1"]},
  11. {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.

  1.  
  1. %%Example - Bad
  2. [rpc:call(node, ets, lookup, [table, K]) || K <- ListOfKeys].
  3.  
  4. %%Example - Good
  5. rpc:call(node, ?MODULE, get_data, [ListOfKeys, []]).
  6. get_data([], Out) -> lists:reverse(Out);
  7. get_data([K|ListOfKeys], Out) -> get_data(ListOfKeys, [ets:lookup(table,K)|Out]).

同理你可以自己改一下:[gen_server:call(Pid,{func,Fun})||Fun<- FunList].

  1. 总之要能一次发消息处理的就不要多次发啦.
  1. 10 不要构造超极大的terms(或者你不可控制大小的terms).
  1. 具体就是如果要遍历ets里面所有的元素,用ets:tab2list/1得出来的结果可能什么非常大,这可怎么办啊!
  1. %% 一次性也出所有元素:不要这样子做
  2. bad_traverse_to_print() ->
  3. [begin print(Person) end||Person <- ets:tab2list(person)],
  4. ok.
  5.  
  6. %%从第一个一直遍历到最后一个:数据要从erts内部搬到process ets很大的时候就效率低
  7. good_traverse_to_print() ->
  8. good_traverse_to_print2(ets:first(person)).
  9.  
  10. good_traverse_to_print2('$end_of_table') ->
  11. ok;
  12. good_traverse_to_print2(Key) ->
  13. [Person] = ets:lookup(person,Key),
  14. print(Person),
  15. good_traverse_to_print2(ets:next(person,Key)).
  16.  
  17. %%分页:最佳实践使用ets:select match MatchSpecets内部实现了把matchspec编译成opcode 然后eval的时候把需要的数据才拷贝到process 大大减少了数据量
  18. best_traverse_to_print() ->
  19. case ets:match(person,'$1',10) of
  20. {PersonList,'$end_of_table'} ->
  21. [begin print(Person) end|| [Person] <- PersonList];
  22. {PersonList,Key} ->
  23. [begin print(Person) end|| [Person] <- PersonList],
  24. best_traverse_to_print2(Key)
  25. end,
  26. ok.
  27. best_traverse_to_print2(Key) ->
  28. case ets:match(Key) of
  29. {PersonList,'$end_of_table'} ->
  30. [begin print(Person) end|| [Person] <- PersonList];
  31. {PersonList,Key2} ->
  32. [begin print(Person) end|| [Person] <- PersonList],
  33. best_traverse_to_print2(Key2)
  34. end.
  35.  
  36. print(Person) ->
  37. io:format("Name~p Phone:~p~n",[Person#person.name, Person#person.phone]),
  38. ok.
第10条和第9条是看似矛盾的,一个说如果可以一次处理完就不要分多次,一个是如果太大就要分多次!注意,如果一个消息体太大了,也要分多次哦。

[Erlang02] 那些经历过的Erlang小坑1-10的更多相关文章

  1. [Erlang11] 那些经历过的Erlang小坑11-20

    11.每次重装系统时都会重新安装Erlang,Ubuntu安装sh秒杀一切. https://gist.github.com/zhongwencool/11174620 12. Erlang Shel ...

  2. mciSendString 的两个小坑

    刚刚修正了自己用的小闹钟的代码. 坑1:REPEAT 选项的作用范围 原来用得好好的,之后选择 .wav 文件,居然不出声音了…… 诶,MCI 肯定支持 .wav 的啊…… 仔细想想,我以前都是选 . ...

  3. 注意Android里TextView控件的一个小坑,用android:theme来设置样式时动态载入的layout会丢失该样式

    注意Android里TextView控件的一个小坑,用android:theme来设置样式时动态载入的layout会丢失该样式 这个坑,必须要注意呀, 比如在用ListView的时候,如果在List_ ...

  4. C#中的Infinity有个小坑

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 昨天家里有事,上网也不方便,就没有推送文章.今天很累,也不长篇大论了.简单介绍一下最近遇到的 ...

  5. 使用EMMET中的小坑

    使用EMMET写HTML的时候,是一个非常爽的事情.但是今天我使用时,发现一个小坑.以前倒也没有注意,不过需要非常的小心. form[action="/process" metho ...

  6. 关于CSS3中transform变换的小坑

    2017年6月30日15:05:46 今天在写一个demo的时候,发现CSS3中transform变换的一个特性. 首先,我先描述一下我发现的情况(问题再现): <div class=" ...

  7. Vue中应用CORS实现AJAX跨域,及它在 form data 和 request payload 的小坑处理

    基本概念部分(一):理解CORS 说道Vue的跨域AJAX,我想先梳理一遍CORS跨域,"跨域资源共享"(Cross-origin resource sharing),它是一个W3 ...

  8. go的变量redeclare的问题,golang的一个小坑

    go的变量声明有几种方式: 1 通过关键字 var 进行声明 例如:var i int   然后进行赋值操作 i = 5 2 最简单的,通过符号 := 进行声明和赋值 例如: i:=5 golang会 ...

  9. MySQL中字段类型为timestamp的小坑

    之前遇到过一个MySQL的字段为timestamp类型的小坑. MySQL中一个字段存储时间类型数据的时候,该字段的类型如果为timestamp类型的话,最多只能存储到2038-01-19 11:14 ...

随机推荐

  1. Null value was assigned to a property of primitive type setter of cn.itcast.oa.domain.Forum.topicCount

    [引用http://m.blog.csdn.net/blog/u013998070/41087351] Null value was assigned to a property of primiti ...

  2. 接口没添加@responseBody注解

    今天在重写springaop小demo时,发现调用接口时,可以在控制台上正常返回结果,但是页面报错,debug半天,可以看到是调用了modelview的时候出错,找不到视图了.. debug的时候控制 ...

  3. 各种编译不通过xcode

    2017-08-24 Apple Mach-O Linker (Id) Error Linker command failed with exit code 1 (use -v to see invo ...

  4. 学习python 多进程和多线程

    ''' 学习多进程和多线程 ''' import multiprocessing def deadLoop(): while True: pass if __name__ == '__main__': ...

  5. sql复杂查询

    内连接 左外连接 Left Outer Join On  ,无论右边是否匹配到,左边的数据都在 右外连接 Right Outer Join On ,无论左边是否匹配到,右边的数据都在 子查询: 将一个 ...

  6. ListView动态改变每一项的高度。

    ListView中每一项的高度默认是相同的,除非超过其预定高度值,否则需要动点手脚. VariableSizedListView 继承 ListView然后重写protected override v ...

  7. 车站(NOIP1998)

    题目链接:车站 这一题,首先你要会推导,推到出式子后,就会像我一样简单AC. 给一张图: 这里,t是第二个车站上车人数. 有什么规律? 其实很好找.有如下规律: 第x车站的人数增量为第x-2车站的上车 ...

  8. HYSBZ - 3676

    模板题.问你一个串里最大的值(回文子串*出现次数) /* gyt Live up to every day */ #include<cstdio> #include<cmath> ...

  9. 20175316盛茂淞-Java第1周学习总结

    20175316盛茂淞 2018-2019-2 <Java程序设计>第1周学习总结 教材学习内容总结 Java入门 1.Java简介(地位,特点) 2.安装JDK,设置系统环境 3.编译J ...

  10. 走进JDK(一)------Object

    阅读JDK源码也是一件非常重要的事情,尤其是使用频率最高的一些类,通过源码可以清晰的清楚其内部机制. 如何阅读jdk源码(基于java8)? 首先找到本地电脑中的jdk安装路径,例如我的就是E:\jd ...