[Erlang02] 那些经历过的Erlang小坑1-10
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 :
所以最好是:明确: 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:
从上面可以看出:
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定义吧
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条是看似矛盾的,一个说如果可以一次处理完就不要分多次,一个是如果太大就要分多次!注意,如果一个消息体太大了,也要分多次哦。
[Erlang02] 那些经历过的Erlang小坑1-10的更多相关文章
- [Erlang11] 那些经历过的Erlang小坑11-20
11.每次重装系统时都会重新安装Erlang,Ubuntu安装sh秒杀一切. https://gist.github.com/zhongwencool/11174620 12. Erlang Shel ...
- mciSendString 的两个小坑
刚刚修正了自己用的小闹钟的代码. 坑1:REPEAT 选项的作用范围 原来用得好好的,之后选择 .wav 文件,居然不出声音了…… 诶,MCI 肯定支持 .wav 的啊…… 仔细想想,我以前都是选 . ...
- 注意Android里TextView控件的一个小坑,用android:theme来设置样式时动态载入的layout会丢失该样式
注意Android里TextView控件的一个小坑,用android:theme来设置样式时动态载入的layout会丢失该样式 这个坑,必须要注意呀, 比如在用ListView的时候,如果在List_ ...
- C#中的Infinity有个小坑
(此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 昨天家里有事,上网也不方便,就没有推送文章.今天很累,也不长篇大论了.简单介绍一下最近遇到的 ...
- 使用EMMET中的小坑
使用EMMET写HTML的时候,是一个非常爽的事情.但是今天我使用时,发现一个小坑.以前倒也没有注意,不过需要非常的小心. form[action="/process" metho ...
- 关于CSS3中transform变换的小坑
2017年6月30日15:05:46 今天在写一个demo的时候,发现CSS3中transform变换的一个特性. 首先,我先描述一下我发现的情况(问题再现): <div class=" ...
- Vue中应用CORS实现AJAX跨域,及它在 form data 和 request payload 的小坑处理
基本概念部分(一):理解CORS 说道Vue的跨域AJAX,我想先梳理一遍CORS跨域,"跨域资源共享"(Cross-origin resource sharing),它是一个W3 ...
- go的变量redeclare的问题,golang的一个小坑
go的变量声明有几种方式: 1 通过关键字 var 进行声明 例如:var i int 然后进行赋值操作 i = 5 2 最简单的,通过符号 := 进行声明和赋值 例如: i:=5 golang会 ...
- MySQL中字段类型为timestamp的小坑
之前遇到过一个MySQL的字段为timestamp类型的小坑. MySQL中一个字段存储时间类型数据的时候,该字段的类型如果为timestamp类型的话,最多只能存储到2038-01-19 11:14 ...
随机推荐
- 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 ...
- 接口没添加@responseBody注解
今天在重写springaop小demo时,发现调用接口时,可以在控制台上正常返回结果,但是页面报错,debug半天,可以看到是调用了modelview的时候出错,找不到视图了.. debug的时候控制 ...
- 各种编译不通过xcode
2017-08-24 Apple Mach-O Linker (Id) Error Linker command failed with exit code 1 (use -v to see invo ...
- 学习python 多进程和多线程
''' 学习多进程和多线程 ''' import multiprocessing def deadLoop(): while True: pass if __name__ == '__main__': ...
- sql复杂查询
内连接 左外连接 Left Outer Join On ,无论右边是否匹配到,左边的数据都在 右外连接 Right Outer Join On ,无论左边是否匹配到,右边的数据都在 子查询: 将一个 ...
- ListView动态改变每一项的高度。
ListView中每一项的高度默认是相同的,除非超过其预定高度值,否则需要动点手脚. VariableSizedListView 继承 ListView然后重写protected override v ...
- 车站(NOIP1998)
题目链接:车站 这一题,首先你要会推导,推到出式子后,就会像我一样简单AC. 给一张图: 这里,t是第二个车站上车人数. 有什么规律? 其实很好找.有如下规律: 第x车站的人数增量为第x-2车站的上车 ...
- HYSBZ - 3676
模板题.问你一个串里最大的值(回文子串*出现次数) /* gyt Live up to every day */ #include<cstdio> #include<cmath> ...
- 20175316盛茂淞-Java第1周学习总结
20175316盛茂淞 2018-2019-2 <Java程序设计>第1周学习总结 教材学习内容总结 Java入门 1.Java简介(地位,特点) 2.安装JDK,设置系统环境 3.编译J ...
- 走进JDK(一)------Object
阅读JDK源码也是一件非常重要的事情,尤其是使用频率最高的一些类,通过源码可以清晰的清楚其内部机制. 如何阅读jdk源码(基于java8)? 首先找到本地电脑中的jdk安装路径,例如我的就是E:\jd ...