Erlang初步编程
一、Erlang的helloWorld
1、从Hello World开始
%%%helloworld.erl
-module(helloworld).
-export([run/1]).
run(Name) -> io:format("Hello World,~w~n",[Name]).
2、保存为helloworld.erl,在Erlang Shell中执行。
Erlang R15B01 (erts-5.9.1) [smp:4:4] [async-threads:0]
Eshell V5.9.1 (abort with ^G)
1> cd("d:/myerl").
d:/myerl
ok
2> c(helloworld).
{ok,helloworld}
3> helloworld:run(liuxuezong).
Hello World, liuxuezong!
ok
4>
说明:
1)、-module(helloworld).
这一行声明了模块helloworld,函数必须定义在模块内,并且模块名称必须与源文件名相同。
2)、-export([run/1]).
这一行声明导出的函数,run/1指的是有一个参数的run函数,因为Erlang允许定义同名的有不同参数的多个函数,通过指定/1来说明要导出的是哪个函数。
3)、run(Name) -> io:format("Hello World, ~w!~n",[Name]).
这一行实现函数定义了,大写开头的是变量Name,调用io模块的format方法输出,~w可以理解成占位符,将被实际Name取代,~n就是换行了。函数定义完了要以句号:“.”结束。
4)、c(helloworld).
执行“c(helloworld).” 编译命令,编译源代码helloworld.erl。
5)、helloworld:run(liuxuezong).
调用函数run:helloworld:run(liuxuezong);
二、子句编程
1、平面几何面积
矩形面积=长 * 宽,圆形面积= PI * R * R。
2、geometry.erl(用来表示几何面积)
%%%geometry.erl
-module(geometry).
-export([area/1]).
area({rectangle, Width, Height}) -> Width * Height;
area({circle, R}) -> math:pi() * R * R;
area({square, X}) -> X * X。
A、C语言实现部分:
#define PI 3.14159265 enum ShapeType {Rectangle, Circle, Square}; typedef Struct tagSHAPE { enum ShapeType kind; union { struct { double width, height; } rectangleData; struct { double radius; } circleData; struct { double side; } squareData; } shapeData; } SHAPE, *LPSHAPE, *PSHAPE; double Area(struct SHAPE * lpShape) { if (lpShape->kind == Rectangle) { double dbWidth, dbHeight; dbWidth = lpShape->shapeData.rectangleData.width; dbHeight = s->shapeData.rectangleData.height; return dbWidth * dbHeight; } else if (lpShape->kind == Circle) { double dbRadius = lpShape->shapeData.circleData.radius; return PI * dbRadius * dbRadius; } else if (lpShape->kind == Square) { double dbSide = lpShape->shapeData.squareData.side; return dbSide * dbSide; } };
B、Java语言实现部分:
abstractclass CShape { abstract double Area(); } class Circle extends CShape { final doublem_dbRadius; Circle(double dbRadius) { m_dbRadius = dbRadius; } double Area() { return Math.PI *m_dbRadius * m_dbRadius; } } class CRectangle extends CShape { final double m_dbHeight; final double m_dbwidth; CRectangle(double dbWidth,double dbHeight) { m_dbHeight = dbHeight; m_dbwidth = dbWidth; } double Area() { returnm_dbwidth *m_dbHeight; } } class CSquare extends CShape { final double m_dbSide; CSquare(double dbSide) { m_dbSide = dbSide; } double Area() { returnm_dbSide *m_dbSide; } }
C、C++代码实现部分:
#define PI 3.14159265 class CShape { public: CShape(); virtual ~CShape(); public: virtual double Area() = 0; }; class CCircle : public CShape { public: CCircle(double dbRadius) { m_dbRadius = dbRadius; } virtual ~CCircle(); public: double Area() { return PI * m_dbRadius * m_dbRadius; } protected: double m_dbRadius; }; class CRectangle : public CShape { public: CRectangle(double dbWidth, double dbHeight) { m_dbHeight = dbHeight; m_dbWidth = dbWidth; } virtual ~CRectangle(); public: double Area() { return m_dbWidth * m_dbHeight; } protected: double m_dbHeight; double m_dbWidth; }; class CSquare : public CShape { public: CSquare(double dbSide) { m_dbSide = dbSide; } virtual ~CSquare(); public: double Area() { return m_dbSide * m_dbSide; } protected: double m_dbSide; };
3、编译并运行geometry.erl
Eshell V5.9.1 (abort with ^G)
1> cd("d:/myerl").
d:/myerl
ok
2> c(geometry).
{ok,geometry}
3> geometry:area({circle, 1.4}).
6.157521601035994
4>
三、顺序编程
1、物品单价
桔子5元/个,报纸8元/份,苹果2元/个,梨9元/个,牛奶7元/瓶。
2、shop.erl(用来表示物品的单价)
%%%shop.erl
-module(shop).
-export([cost/1]).
cost(oranges) -> 5;
cost(newspaper) -> 8;
cost(apples) -> 2;
cost(pears) -> 9;
cost(milk) -> 7.
3、编译并运行shop.erl
1> c(shop).
{ok,shop}
2> shop:cost(oranges).
5
2> shop:cost(milk).
7
4、物品数量
桔子4个,报纸1份,苹果10个,梨6个,牛奶3瓶。
用列表表示为:
Buy=[{oranges,4},{newspaper,1},{apples,10},{pears,6},{milk,3}].
5、shop1.erl(用来表示物品的总价)
%%%shop1.erl
-module(shop1).
-export([total/1]).
total([{What,N}|T]) -> shop:cost(What) * N + total(T);
total([]) -> 0.
6、编译并运行shop1.erl
1> c(shop1).
{ok,shop1}
2> shop1:total([]).
0
2> shop1:total([{milk, 3}]).
21
3> shop1:total([{pears, 6}]).
54
4> shop1:total([{milk, 3},{pears, 6}]).
75
四、匿名函数(funs)
funs是匿名函数。他们之所以被这样称呼是因为他们没有名字。让我们作点小的尝试,首先我们定义一个 fun 并且将它分配给一个变量Z:
1> Z = fun(X) -> 2 * X end.
2>
#Fun<erl_eval.6. 82930912> 当我们定义了一个fun,Erlang的shell会打印出 #Fun<...>, 其中...是一些古怪的数字。现在不需要担心这个。 然后我们能用这个fun只作一件事,并且给它附加一个参数,比如:
3> Z(2).
4> 4
Z不是一个适合的名字,一个更好的名字将是Double,它描述了这个fun做什么:
3> Double = Z.
#Fun<erl_eval.6. 82930912>
4> Double(4). 8
Funs 可以有很多参数。我们能够写一个函数来计算直角三角形的斜边,比如:
5> Hypot = fun(X, Y) -> math:sqrt(X*X + Y*Y) end. #Fun<erl_eval.12.82930912>
6> Hypot(3,4). 5.00000
如果参数的个数不对,你将会获得一个错误:
** exception error: interpreted function with arity 2 called with one argument
下面是执行过程:
Erlang R15B01 (erts-5.9.1) [smp:4:4] [async-threads:0]
Eshell V5.9.1 (abort with ^G)
1> Z = fun(X) -> 2 * X end.
#Fun<erl_eval.6.82930912>
2> Z(2).
4
3> Double = Z.
#Fun<erl_eval.6.82930912>
4> Double(4).
8
5> Hypot = fun(X, Y) -> math:sqrt(X*X + Y*Y) end.
#Fun<erl_eval.12.82930912>
6> Hypot(3, 4).
5.0
7> Hypot(3).
** exception error: interpreted function with arity 2 called with one argument
这里就有一个温度转换函数,它在华氏和摄氏间转换:
8> TempConvert = fun({c,C}) -> {f, 32 + C*9/5};
8> ({f,F}) -> {c, (F-32)*5/9}
8> end.
#Fun<erl_eval.6.82930912>
9> TempConvert({c,100}).
{f,212.0}
10> TempConvert({f,212}).
{c,100.0}
11>
等价过程:
1、tempconvert.erl(用来摄氏度与华度之间转换)
tempconvert.erl
%%%tempconvert.erl
-module(tempconvert).
-export([convert/1]).
convert([{c,C}]) -> {f,32 + C *9/5};
convert([{f,F}]) -> {c, (F-32)*5/9}.
2、编译并运行tempconvert.erl
1> c(tempconvert).
{ok,tempconvert}
2> tempconvert:convert([{c,100}]).
{f,212.0}
3> tempconvert:convert([{f,212}]).
{c,100.0}
五、流程控制
1、libmisc.erl(用来简单循环计算)
%%%libmisc.erl
-module(libmisc).
-export([for/3]).
for(Max, Max, F) -> [F(Max)];
for(I, Max, F) -> [F(I)|for(I+1, Max, F)].
2、编译并运行libmisc.erl(两种函数输入方法)
2> c(libmisc).
{ok,libmisc}
3> libmisc:for(1,10,fun(I) -> I end).
[1,2,3,4,5,6,7,8,9,10]
4> M = fun(I) -> I end.
#Fun<erl_eval.6.82930912>
5> libmisc:for(1,10,M).
[1,2,3,4,5,6,7,8,9,10]
6> N = fun(I) -> I * I end.
#Fun<erl_eval.6.82930912>
7> libmisc:for(1,10,N).
[1,4,9,16,25,36,49,64,81,100]
8>
3、递归过程
for(1,10,M)
=> [F(1)| for(2,10,M)]
=> [F(1),F(2)| for(3,10,M)]
=> [F(1),F(2),F(3)| for(4,10,M)]
=> [F(1),F(2),F(3),F(4)| for(5,10,M)]
=> [F(1),F(2),F(3),F(4),F(5)| for(6,10,M)]
=> [F(1),F(2),F(3),F(4),F(5),F(6),| for(7,10,M)]
=> [F(1),F(2),F(3),F(4),F(5),F(6),F(7)| for(8,10,M)]
=> [F(1),F(2),F(3),F(4),F(5),F(6),F(7),F(8)| for(9,10,M)]
=> [F(1),F(2),F(3),F(4),F(5),F(6),F(7),F(8),F(9)| for(10,10,M)]
=> [F(1),F(2),F(3),F(4),F(5),F(6),F(7),F(8),F(9),F(10)]
l 说明:
对这个例子而言,计算 for(1,10,F) 就是建立一个列表 [F(1), F(2), ..., F(10)]。
模式匹配在这个循环中是如何工作的?在第一个子句中仅仅会匹配第一个参数和第二个参数相同的情况。 所以当我们调用for(10,10,F),第一个子句将匹配绑定Max到10,并且结果将是列表[F(10)]。
如果我们调用 for(1,10,F),第一个子句将不会被匹配,因为Max不能同时等于1和10。 因为这个原因,第二个子句将会匹配并且绑定 I -> 1和 Max ->10; 通过递归,我们得知返回的结果是:[F(1),F(2),...,F(10)]。
l 执行:
A、计算从1到10的整数列表:
3> libmisc:for(1,10,fun(I) -> I end).
[1,2,3,4,5,6,7,8,9,10]
B、计算从1到10的平方列表:
6> N = fun(I) -> I * I end.
#Fun<erl_eval.6.82930912>
7> libmisc:for(1,10,N).
[1,4,9,16,25,36,49,64,81,100]
8>
如果你有更多的经验,你将发现通过建立你自己的流程控制结构,能够显著减少你的程序尺寸或者使它们更清晰。因为你可以根据解决问题的需要,建立严密合理的流程控制结构,还因为你可以不受限制的通过小的固定的流程控制结构设置到你的程序中。
六、列表处理
1、mylists.erl(用来求和)
%%%mylists.erl
-module(mylists).
-export([sum/1]).
sum([H|T]) -> H + sum(T);
sum([]) -> 0.
注意:sum中的两个子句的次序是无关紧要的。这是因为第一个子句匹配一个非空的列表,而第二个是空的列表, 并且两个子句是相互排斥的。
2、编译并运行mylists.erl
2> c(mylists).
{ok, mylists}
3> L = [1,3,10].
[1,3,10]
4> mylists:sum(L).
14
5>
3、递归过程
sum([1,3,10])
=> = 1 + sum([3,10])
=> = 1 + 3 + sum([10])
=> = 1 + 3 + 10 + sum([])
=> = 1 + 3 + 10 + 0
=> = 14
4、物品数量
桔子4个,报纸1份,苹果10个,梨6个,牛奶3瓶。
用列表表示为:
Buy=[{oranges,4},{newspaper,1},{apples,10},{pears,6},{milk,3}].
5、编译并运行(三种函数输入方法)
%%%shop2.erl
-module(shop2).
-export([total/1]).
-import(lists, [map/2, sum/1]).
total(L) -> sum(map(fun({What, N}) -> shop:cost(What) * N end, L)).
也可以定义成如下格式:
%%%shop2.erl
-module(shop2).
-export([total/1]).
-import(lists, [map/2]).
total(L) -> mylists:sum(map(fun({What, N}) -> shop:cost(What) * N end, L)).
还可以定义成如下格式:
%%%shop2.erl
-module(shop2).
-export([total/1]).
-import(lists, [map/2, sum/1]).
-import(shop, [cost/1]).
total(L) ->sum(map(fun({What, N}) -> cost(What) * N end, L)).
Note also the use of the -import and -export declarations in the module:
• The declaration -import(lists, [map/2, sum/1]). means the function map/2 is imported from the module lists, and so on. This means we can write map(Fun, ...) instead of lists:map(Fun, ...). cost/1 was not declared in an import declaration, so we had to use the ―fully qualified‖ name shop:cost.
• The declaration -export([total/1]) means the function total/1 can be called from outside the module shop2. Only functions that are exported from a module can be called from outside the module.
erts-5.9.1) [smp:4:4] [async-threads:0]
Eshell V5.9.1 (abort with ^G)
1> cd("d:/myerl").
d:/myerl
ok
2> c(shop2).
{ok,shop2}
3> Buy = [{oranges,4},{newspaper,1},{apples,10},{pears,6},{milk,3}].
[{oranges,4},{newspaper,1},{apples,10},{pears,6},{milk,3}]
4> L1=lists:map(fun({What,N}) -> shop:cost(What) * N end, Buy).
[20,8,20,54,21]
5> lists:sum(L1).
123
6> M = fun({What,N}) -> shop:cost(What) * N end.
#Fun<erl_eval.6.82930912>
7> L2 = lists:map(M, Buy).
[20,8,20,54,21]
8> mylists:sum(L2).
123
9>shop2:total(Buy).
123
七、列表分解
1、水落石出
当列表包含的表达式没有用funs、maps或filters等函数,这使得我们的程序,甚至更短及更容易去理解。
先让我们从一个例子开始吧,假设有一个列表L:
2> L = [1,2,3,4,5].
[1,2,3,4,5]
假设我们要加倍列表中的每个元素,之前我会提醒你这样做:
3> lists:map(fun(X) -> 2 * X end, L).
[2,4,6,8,10]
但有一个更容易的方式去理解列表的使用:
4> [2*X || X<-L].
[2,4,6,8,10]
符号[F(X)||X<-L]意思是:列表中的 F(X),其中X是从列表L中获取。因此,[2*X||<X-L],列表中的2*X,其中X是从列表L中获取。我们在Shell写入一些表达式,看 看会发生什么变化,开始定义一个买“Buy”变量:
5> Buy=[{oranges,4},{newspaper,1},{apples,10},{pears,6},{milk,3}].
[{oranges,4},{newspaper,1},{apples,10},{pears,6},{milk,3}].
现在我们把原始列表中的每项数目加倍:
6> [{Name, 2*Number} || {Name, Number} <- Buy].
[{oranges,8},{newspaper,2},{apples,20},{pears,12},{milk,6}]
注意, 元组右侧(||)符号的{Name,Number}就是模式匹配每个列表元素中Buy。元组的左边,{Name,2*Number}是一个构造。
假如我们想去计算购物列表的费用情况,我们可以这么做,首先用每个水果的价格替换其名称:
7> [{shop:cost(A), B} || {A, B} <- Buy].
[{5,4},{8,1},{2,10},{9,6},{7,3}]
现在可以乘以数字:
8> [shop:cost(A) * B || {A, B} <- Buy].
[20,8,20,54,21]
最后总和:
9> lists:sum([shop:cost(A) * B || {A, B} <- Buy]).
123
总之,我们可以编写一个短而简洁的列表分析函数:
total(L) -> lists:sum([shop:cost(A) * B || {A, B} <- L]).
2、编译并运行
%%%shop3.erl
-module(shop3).
-export([total/1]).
-import( [cost/1]).
total(L) -> lists:sum([cost(A) * B || {A, B} <- L]).
erts-5.9.1) [smp:4:4] [async-threads:0]
Eshell V5.9.1 (abort with ^G)
1> cd("d:/myerl").
d:/myerl
ok
2> c(shop3).
{ok,shop3}
3> Buy = [{oranges,4},{newspaper,1},{apples,10},{pears,6},{milk,3}].
[{oranges,4},{newspaper,1},{apples,10},{pears,6},{milk,3}]
4> shop3:total(Buy).
123