Erlang 初学者技巧及避免的陷阱
1. 传参或在匿名函数内慎用self()
通常在做消息传递或新建进程的时候我们需要将当前进程的Pid发给目标进程以便接收返回信息,但初学者不留意容易犯以下错误
spawn(fun() ->
loop(self(), gen_tcp:accpet(...))
end).
fun这段代码在本进程内是不会预先执行的,代码会原封不动传给目标进程。当实际调用self()的时候,获取的实际不是本进程的Pid了。
所以建议当需要传递当前进程Pid或者其他当前进程类似函数的时候,先求值再传递。保持良好习惯就可以避免类似的坑。
Pid = self(),
Socket = gen_tcp:accpet(...),
spawn(fun() ->
loop(Pid, Socket)
end).
2. Message Passing支持发送Socket、文件句柄等对象引用消息
稍微了解Erlang的同学都知道Erlang消息传递目标进程接收的实际是发送进程消息的一个拷贝副本,对于存储类数据这个没有问题。
当对于Socket,FileHandler这类对象的时候,事实上也是可以传递并能起到预期效果的。
(以下Demo代码来自Erlang的Mailing List)
%This is an example of how trivial writing a FTP-like client in Erlang. %On machine 1.
File = file:open(FileName, [read]).
PidOfProcessOnMachine2 ! {remote_file, File}. %On machine 2.
receive
{remote_file, File} ->
{ok, Data} = file:read(File, 1000000),
write_to_local_disk(Data)
end
3. 在不需追踪函数状态的时候,尽量使用尾递归
众所周知,Erlang是没有循环结构的,我们要想实现循环基本是靠递归实现的。但又众所周知,函数递归是要不停压栈的,我们要想实现 while(true)怎么办?
答案就是尾递归,许多这类语言都已在编译器层面实现了尾递归优化,在编译器识别出尾递归后,编译器会直接丢掉当前函数状态信息变成做跳转,这里汇编层面其实就和循环结构很类似了。
但尾递归也有个缺点,就是函数状态没法保留了,所以有时候复杂情况需要追踪调试函数栈状态的时候就不能用尾递归。但其实在Erlang这类函数式语言里,尾递归大多数情况是用来实现循环,所以实际上也不需要留意函数栈。
4. 动态创建原子要十分谨慎
Erlang里有一种特殊的结构叫做原子(atom),一般用来做类似传统语言里的常量作用或用来辅助模式匹配。
Erlang默认有几百万个内置原子,但是Erlang除非整个VM退出了,否则是不会对任何原子做GC的。
所以有时候我们为了匹配会使用 list_to_atom 函数去动态创建原子,但这是很危险的,甚至有可能导致内存泄露。
所以除非你自己很清醒自己的所作所为并且考虑到别人对你代码的调用情况,否则尽量不要动态创建原子。
但静态创建原子是没问题的,毕竟你手动打的数目总是极其有限,比如:
receive
{my_own_atom, Var} -> pass.
end.
但谨慎出现以下代码:
% On machine 1 Type = recv(),
Msg = recv(),
Machine2 ! {list_to_atom(Type), Msg}. % On machine 2 receive
{type1, Msg} -> pass;
{type2, Msg} -> pass
end.
这里的Type很容易成为受攻击的对象。
5. 巧用原子作模式匹配
经常我们要解析协议或者匹配同等元素个数的元组列表,这时候可以用原子来进行区分实现模式匹配。
% Receiver
Packet = recv(Socket), % Get TCP Packet case Packet of
[new_user, "||", Username] ->
pass;
[new_board, "||"] ->
pass.
end. % Sender send([new_user, "||", Username]).
6. 特殊字符使用$开头,如$\n。
这个没什么好说,有需要去查看对应手册。
Line = io:get_line("Input: "),
S = string:strip(Line, both, $\n).
7. 大型数据慎用消息传递,有必要可考虑ETS表做进程共享
Erlang有个口号叫“小消息 大计算”,所以使用Erlang期望传递的消息本来就是小消息。但我们经常也不可避免需要进程间共享一些大消息,这时候我们可以考虑从借助进程字典或ETS表。但其实Erlang的这种进程间只能通过MP通信的机制也逼迫我们在设计程序时要求每个进程的角色分工很明确。
8. 跨机器间的进程消息发送要小心,小型消息频率过高TCP头部的发送代价也高。
这个其实还是见仁见智,根据实际业务情况而定。如果真的是因为TCP消耗引起的性能问题,就要考虑本地开个进程做代理,缓存一定的消息,定时批量发送。其实就是做个缓存队列。
可参见此文:Erlang中频繁发送远程消息要注意的问题
9. 函数传参记得参数相对应
这个没啥好说的,直接看代码。尽管fun匿名函数处理不需要参数,但for_内的F是个一元函数,所以也要至少用个_匹配符去代表那里有个参数。Erlang有另一种元调用方式apply(有点像JavaScript),apply传参是用个列表Args = [...],适用于较灵活的一些调用,比如spawn,但这种反射式调用一般效率都会较低,Erlang的apply据说比本地直接调用F()要慢上6 - 10倍。
for_(I, Max, F) ->
case I == Max of
false -> [F(I) | for_(I + 1, Max, F)];
true -> [F(Max)]
end. for_(0, 5, fun(_) ->
io:write("Hello Erlang")
end).
10. 列表操作要谨慎
很多有其他类函数式语言(Python、Coffee等)经历的同学会很喜欢列表结构,但是Erlang的列表相对比较不同。
首先,Erlang的所有“变量”都是不可变也不可二次绑定的,所以想像Python那样自由操作list是不可能的,必须每次修改返回一个新变量。
列表右侧增长: [1,2,3] ++ [4,5] = [1,2,3,4,5], "Hello" ++ " " ++ "Erlang" = "Hello Erlang"
++其实是lists:append/2的syntax sugar,切记和动态创建原子一样,可偶尔为之,但不要放任列表动态右侧增长。++会复制左边的元素,会使复制多次,最后导致平方倍的复杂度。
列表左侧增长:[1,2,3 | [4,5]] = [1,2,3,4,5]
不要和 [[1,2,3] | [4,5]] = [[1,2,3],4,5] 搞混。
列表length方法是O(N)时间复杂度,慎用length(List),很多需求可以用模式匹配实现。
case List of
[Elem | _] -> process(Elem);
[] -> processEmptyList()
end.
但是元组和二进制串的size方法却是O(1)的,可放心使用。
Erlang 初学者技巧及避免的陷阱的更多相关文章
- Delphi初学者应小心的六大陷阱
Delphi初学者应小心的六大陷阱 作者:子夜编译 初学DelphiI的人,由于各种原因,对DelphiI中的许多概念不能很好的理解,并由此带来了许多的问题,或者是开发出的程序稳性不好 ...
- JavaScript中8个常见的陷阱
译者按: 漫漫编程路,总有一些坑让你泪流满面. 原文: Who said javascript was easy ? 译者: Fundebug 为了保证可读性,本文采用意译而非直译.另外,本文版权归原 ...
- 《Erlang程序设计(第2版)》
<Erlang程序设计(第2版)> 基本信息 作者: (瑞典)Joe Armstrong 译者: 牛化成 丛书名: 图灵程序设计丛书 出版社:人民邮电出版社 ISBN:9787115354 ...
- 如何写复杂的SQL
经常有人问我那非常复杂的sql是怎么写出来的,我一直不知道该怎么回答. 因为虽然我写这样的sql很顺手,可是我却不知道怎么告诉别人怎么写. 很多人将这个问题归结为天赋,我却不这么看,我 ...
- JavaScript常用,继承,原生JavaScript实现classList
原文链接:http://caibaojian.com/8-javascript-attention.html 基于 Class 的组件最佳实践(Class Based Components) 基于 C ...
- JavaScript中8个容易犯的错误
这里dbestech针对JavaScript初学者给出一些技巧和列出一些陷阱. 1. 你是否尝试过对数组元素进行排序? JavaScript默认使用字典序(alphanumeric)来排序.因此,[1 ...
- Golang核心编程
源码地址: https://github.com/mikeygithub/GoCode 第1章 1Golang 的学习方向 Go 语言,我们可以简单的写成 Golang 1.2Golang 的应用领域 ...
- C和C指针小记(十五)-结构和联合
1.结构 1.1 结构声明 在声明结构时,必须列出它包含的所有成员.这个列表包括每个成员的类型和名称. struct tag {member-list} variable-list; 例如 //A s ...
- 如何做Go的性能优化?(转)
Go的性能优化其实总的来说和C/C++等这些都差不多,但也有它自己独有的排查方法和陷阱,这些都来源于它的语言特性和环境. 1.性能优化前提——任何好的东西都是在正确的前提上 代码界的很多事是和我们生活 ...
随机推荐
- 使用KRPano资源分析工具解密被加密的XML
软件交流群:571171251(软件免费版本在群内提供) krpano技术交流群:551278936(软件免费版本在群内提供) 最新博客地址:blog.turenlong.com 限时下载地址:htt ...
- C++内存管理
1. 栈(Stack): 位于函数内的局部变量(包括函数实参),由编译器负责分配释放,函数结束,栈变量失效.2. 堆(Heap): 由new申请的内存,由delete负责 ...
- APM程序分析-Control_rtl.cpp
bool Copter::rtl_init(bool ignore_checks) { if (position_ok() || ignore_checks) { rtl_build_path(!fa ...
- [网络流24题] 搭配飞行员(cogs 14)
[问题描述] 飞行大队有若干个来自各地的驾驶员,专门驾驶一种型号的飞机,这种飞机每架有两个驾驶员,需一个正驾驶员和一个副驾驶员.由于种种原因,例如相互配合的问题,有些驾驶员不能在同一架飞机上飞 ...
- pygame学习笔记
pygame参考文档pdf版:pygame API html版 pygame API 石头剪子布的简单小游戏,待改进的地方,自适应大小.感兴趣的小伙伴可以依据get_surface()返回值(即当前窗 ...
- 3.2 配置构建Angular应用——简单的笔记存储应用
本节我们会通过构建一个简单的笔记存储应用(可以载入并修改一组简单的笔记)来学习如何应用Angular的特性.这个应用用到的特性有: 在JSON文件中存储笔记 展示.创建.修改和删除笔记 在笔记中使用M ...
- 手机开发中的AP与BP的概念
转自:http://blog.csdn.net/macong01/article/details/15504611 手机的AP和BP: AP:ApplicationProcessor,即应用芯片 BP ...
- 收缩SQL Server数据库文件
收缩整个数据库 DBCC SHRINKDATABASE (dbName) 收缩单个文件DBCC SHRINKFILE (FileName) 查找所有的文件名称USE dbNameEXEC sp_hel ...
- Oracle sql连接
inner-join left-outer-join right-outer-join full- ...
- Linux学习笔记(9)-守护进程
明天学这个!! ---------------------------------------------------------- 守护进程(Daemon)是运行在后台的一种特殊进程.它独立于控制终 ...