当前位置:数据分析 > [Erlang02]我经历过的那些Erlang陷阱1-10

[Erlang02]我经历过的那些Erlang陷阱1-10

  • 发布:2023-10-06 04:57

-->

1。如果guard发生错误,不会报错,只会报错!

情况 1=:1 个
当不是 erlang:length(t) =:= 1 时为 true,否则为 true ->
好的;
_ ->
错误
结束了。

结果是:错误

计算受保护表达式中t(​​原子)的长度时会出现错误。应该会崩溃,但是因为在protected表达式中,protected表达式的计算默认在出错后结束并返回false。这就是受保护的表达式不接受复杂函数的原因。 ,这也是只能使用erlang的bif来完成的原因之一。

2。如果尝试catch时不写catch类型,则默认是throw类型!

try_catch(值) ->
试试
案例
的值 错误 -> erlang:error({error,plz_catch_me});
抛出 -> erlang:抛出({抛出,oh_get_you});
退出 -> erlang:exit({exit,sorry_by_mistake})
结束
抓住
T -> T
结束了。

结果:

所以最好明确一点:Catch throw:T –> { throw,T};错误:T –> {错误,T};退出:T –> {退出,T} 结束。

3。在保护表达式中使用 erlang:length/1 时要小心! (遍历列表,时间长度是可变的)

%%这样写所花费的时间与列表长度的平方成正比:不要这样做
foo(List) 当长度(List) >0 ->
做某事;
foo(_) ->
完成了。
%%使用匹配模式进行任意长度确定
更好([一]) ->
do_something_one();
更好([一,二])->
do_something_two();
更好([一,二|_])->
do_something_big();
更好([]) ->
do_something_empty()
结束了。

提示:如果想判断List是否为非空List,可以使用case List of [_|_] –> do_something(); _ –> 完成。

4。 ++只是lists:append/2的别名:如果你想使用它,一定要使用ShortList++LongList! (可以记为长与短短与长的反义词...每次使用都会条件反射性地思考)

%% 不要这样做
naive_reverse([H|T]) ->
naive_reverse(T)++[H];
naive_reverse([]) ->
[].

这是反转列表的最低效的方法。由于 ++ 运算符复制其左操作数,因此结果将被一次又一次地复制......导致二次复杂度。

这是反转列表的最低效的方法。 “++”将复制左侧的元素。这将导致多个副本,从而导致复杂度平方。

但另一方面:以下用法也可以:

%% 好的
naive_but_ok_reverse([H|T], Acc) ->
naive_but_ok_reverse(T, [H]++Acc);
naive_but_ok_reverse([], Acc) ->
加速

这不是一个非常糟糕的尝试。每个列表元素仅复制一次。递增的 Acc 位于 ++ 的右侧。不会每次都复制

当然,最佳做法仍然是以下:

%% 最好做
vanilla_reverse([H|T], Acc) ->
vanilla_reverse(T, [H|Acc]);
vanilla_reverse([], Acc) ->
加速

这个比上面的效率高一点。您根本不需要创建列表元素,只需直接复制它即可(或者:如果编译器不将 [H]++Acc 重写为 [H|Acc] ,那会更有效)。

5。 receive 和 case 之间有很大的区别,尽管它们的写法类似:

case_test(值) ->
案例
的值 1 -> 好的;
2 -> 错误
结束了。
receive_test(Value)当 Value>2 ->
PID=spawn(fun()->
接收
{msg,1} ->
好的;
{msg,2} ->
错误
结束
完),
[开始PID! {msg,ValueT} 结束 ||ValueT<-lists:seq(3,Value)],
PID.

结果:

从上面可以看到:

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

5.1 接收在没有匹配消息时将阻塞。只要邮箱中没有匹配的邮件,就会在等待期间挂起。只有当有新消息到达时它才会被唤醒。每次执行时,recieve都会先检查最旧的消息(位于队列的头部),尝试按照case表达式中的方式进行匹配,如果没有找到,则继续下一条消息,如果当前匹配成功,并且保护为真(如果有),则该消息将从邮箱中删除,并且该子句的相应文本将被执行。如果没有找到合适的消息,就会等待直到超时(如果有,则在Times之后)。

6。当erl使用-noshell –noinput启动节点时,我看不到它,也无法输入它。我该如何调试它?使用-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> 节点().
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 在远程节点上调用q(),两个节点都会退出!它是可怕的。最好重写 user_default.erl 中的 q() 函数,使其不执行 init: stop()。

6.2 window下使用werl代替erl;

6.3 erl 支持自定义参数。例如,如果您编写 erl –rmsh test,则不会出现错误。如果一不小心写错了,要查很久……

提示: 已经设置了两个节点A和B。如果想用A控制B,可以在A中使用 Ctrl+G r NodeA j 2 操作。具体参见:学习一些erlang远程shell。

7。如果有一个像这样从不知名的地方传递过来的ArgList:“[test1,test2,test3]”,怎么用它来动态执行呢?

场景:了解方法调用的方式:Method使用erlang:apply(Module,Method,ArgList)来调用产生结果。此时ArgList不符合格式:

%% 字符串 = "[测试1,测试2,4]。"注意最后的结束语!
string_to_args(字符串) ->
{ok, Scan1, _} = erl_scan:string(String),
{ok,P}=erl_parse:parse_exprs(Scan1),
{值,值,[]} = erl_eval:exprs(P, []),
值。

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

可以参考

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

>erl -测试 erlang1 -测试你好 -test1 test1 >init:get_argument(测试).
{好的,[["erlang1"],["你好"]] >init:get_arguments()。
[{root,["C:\\Program Files\\erl6.0"]},
{程序名称,["erl"]},
{home,["C:\\Users\\**"]},
{测试,["erlang1"]},
{测试,["你好"]}]

8.1 不要将参数定义为字符串:例如“hello world”

8.2 如果这用于启动应用程序,则不要期望使用此自定义参数。使用config来定义它

应用程序通常不应使用命令行标志进行配置,而应使用应用程序环境。有关详细信息,请参阅设计原则章节中的配置应用程序

9。使用RPC时,一定要记住你是分布式的,并且时刻注意你处于哪个进程!

例如:将rpc:call放在循环内部和外部会得到不同的效率反馈。以下示例的结果是等效的,但第一个将发出多次调用,而第二个将仅发出一次调用。

%%示例 - 不好
[rpc:调用(节点,ets,查找,[表,K])|| K <- 键列表]。 %%示例 - 好
rpc:call(节点, ?MODULE, get_data, [ListOfKeys, []]).
get_data([], Out) -> 列表:reverse(Out);
get_data([K|ListOfKeys], Out) -> get_data(ListOfKeys, [ets:lookup(table,K)|Out]).

同样,你可以自己更改它:[gen_server:call(Pid,{func,Fun})||Fun<- FunList]。

简而言之,如果你可以发送一次消息并处理它,就不要发送多次。 10 不要构造非常大的术语(或您无法控制大小的术语)。 具体来说,如果要遍历ets中的所有元素,使用ets:tab2list/1得到的结果可能会很大。我应该怎么办?
%% 一次性提取所有元素:不要这样做
bad_traverse_to_print() ->
[开始打印(人)结束||人<- ets:tab2list(人)],
好的。 %%从第一个到最后一个遍历:数据需要从erts移动到process。 ets大时效率低
good_traverse_to_print() ->
good_traverse_to_print2(ets:first(person)). good_traverse_to_print2('$end_of_table') ->
好的;
good_traverse_to_print2(键)->
[人] = ets:lookup(人,键),
打印(人),
good_traverse_to_print2(ets:next(person,Key))。 %%分页:最佳实践是使用ets:select match MatchSpec:ets内部将matchspec编译成opcode,然后在eval时将需要的数据复制到进程中,这样就大大减少了数据量
best_traverse_to_print() ->
case ets:match(person,'$1',10) of
{PersonList,'$end_of_table'} ->
[开始打印(人)结束|| [人物] <- 人物列表];
{PersonList,Key} ->
[开始打印(人)结束|| [人物] <- 人物列表],
best_traverse_to_print2(密钥)
结束了,
好的。
best_traverse_to_print2(键)->
case ets:
的匹配(键) {PersonList,'$end_of_table'} ->
[开始打印(人)结束|| [人物] <- 人物列表];
{PersonList,Key2} ->
[开始打印(人)结束|| [人物] <- 人物列表],
best_traverse_to_print2(Key2)
结尾。 打印(人)->
io:format("姓名~p 电话:~p~n",[Person#www.sychzs.cn, Person#www.sychzs.cn]),
好的。 -->

相关文章

热门推荐