Erlang(二)函数与模块

函数与模块

模块是存放代码的地方

模块是Erlang的基本代码单元,保存在扩展名.erl文件中,模块包含函数,可以顺序或并行运行。

geometry.erl

-module(geometry)
-export([area/1])

第一行是模块声明,模块名必须与存放改模块的文件名相同

第二行是导出声明,Name/N带有N个参数的函数Name,N称为函数的元数

未从模块里导出的函数只能在模块内调用。(导出=》公有,未导出=》私有)

area({rectangle,Width,Height})->Width*Height;
area({square,Side}) -> Side * Side.

子语句之间用;隔开,句号和空白结果

子语句,头部和主体用 ->隔开

头部包含一个函数,后接零个或多个模式,主体包含一列表达式,在头部模式与调用参数成功匹配时执行。

函数没有显示的返回语句,返回值就是字句主体里最后一条表达式的值。

代码测试

-module (geometry).
-export ([test/0, area/1]).

test() ->
		 12 = area({rectangle, 3, 4}),
		 144 = area({square, 12}),
		 tests_worked.

area({rectangle, Width, Height}) -> Width * Height;
area({square, Side}) -> Side * Side.

1> c(geometry).   
{ok,geometry}
2> geometry:test().
tests_worked

扩展程序

加;继续扩展

符号

逗号,分隔函数调用,数据构造和模式中的参数

分号,分隔字句

句号,分隔函数整体,以及shell表达式

fun基本的抽象单元

函数式编程语言,表示函数可以作为其他函数的参数,也可以返回函数。操作其他函数的函数称为高阶函数。

  • 对列表中每一个元素执行相同的操作,list:map/2list:filter/1

  • 创建自己的控制抽象。Erlang没有for循环,可以创建for循环

  • 实现可重入解析代码、解析组合器或惰性求值器。

Double = fun(X) -> 2*X end.

计算直角三角形的斜边

Hypot = fun(X,Y) -> math:sqrt(X*X + Y*Y) end.

fun可以有不同的字句

TempConvert = fun({c, C}) -> {f, 32 + C*9/5};
    fun({f, F}) ->{c, (F-32)*5/9}
    end.

以fun作为参数的函数

Even=fun(X) -> (X rem 2) =:= 0 end.
lists:map(Even,[1,2,3,4,5,6,7,8]).
lists:filter(Even,[1,2,3,4,5,6,7,8]).
[2,4,6,8]

一次性调用整个列表执行某种操作,一次一列表式操作

返回fun的函数

Mult = fun(Times) -> (fun(X) -> X*Times end) end.
Triple = Mult(3).
Triple(5).
15

定义控制抽象

没有if,switch,for,while

想要额外的控制结构,可以自己创建。一切都是用匹配模型和高阶函数编写的。

for(Max,Max,F) -> [F(Max)];
for(I,Max, F) -> [F(I)|for(I+1),Max,F]
lib_misc:for(1, 10, fun(I) -> I * I end).
[1,4,9,16,25,36,49,64,81,100]

列表处理

sum

-module (mylists).
-export ([sum/1]).

sum([H|T]) -> H + sum(T);
sum([]) -> 0.

L=[1,3,10].
[1,3,10]
mylists:sum(L).

map(_, []) -> [];
map(F, [H|T]) -> [F(H)|map(F, T)].

列表推导

L=[1,2,3,4,5].
list:map(fun(X)->2*X end, L).

等价于

[2*X || X <- L].
[F(X) || X <- L].

F(X)组成的列表(X从列表L中提取)

total(L) ->
    lists:sum([shop:cost(A)*B] || {A,B} <- L]).

简化map

map(F,L) -> [F(X) <- L].

列表推导表达式

生成器

Pattern <- ListExpr(列表表达式)

位串

BitStringPattern <= BitStringExpr

过滤器1、4的结构一致

[X || {a,X} <- [{a,1},{b,2},{c,3},{a,4},hello,"wow"]].  [1,4]

quickSort

-module (lib_misc).
-export ([qsort/1]).

qsort([])->[];
qsort([Pivot|T])->
    qsort([X || X <- T, X < Pivot])
    ++ [Pivot] ++
    qsort([X || X <- T, X >= Pivot]).

1> c(lib_misc).
{ok,lib_misc}
2> L=[23,6,2,8,27,400,78,56,73,85,11].
[23,6,2,8,27,400,78,56,73,85,11]
3> lib_misc:qsort(L).
[2,6,8,11,23,27,56,73,78,85,400]

比格拉斯三元数组

A*A=B*B + C*C
pythag(N) ->
    [{A,B,C} ||
        A <- lists:seq(1,N),
        B <- lists:seq(1,N),
        C <- lists:seq(1,N),
        A+B+C =< N,
        A*A + B*B =:= C*C
    ].

lists:seq(1,N)返回一个包含1到N所有整数的列表

lib_misc:pythag(30).
[{3,4,5},{4,3,5},{5,12,13},{6,8,10},{8,6,10},{12,5,13}]

回文构词

perms([]) -> [[]];
perms(L) -> [[H|T] || H <- L, T <- perms(L -- [H])].
lib_misc:perms("cats").
["cats","cast","ctas","ctsa","csat","csta","acts","acst",
"atcs","atsc","asct","astc","tcas","tcsa","tacs","tasc",
"tsca","tsac","scat","scta","sact","satc","stca","stac"]

内置函数

list_to_tuple/1

关卡

通过使用关卡,可以对某个模式里的变量执行简单的测试和比较

max(X,Y) when X > Y -> X;
max(X,Y) -> Y.

通过when关键字引入

关卡序列,指单一或一系列关卡,用;分隔,对于关卡序列G1;G2;G3...;Gn,只要其中一个为true,值为true。关卡有一系列关卡表达式组成,用,分隔。只有所在的所有关卡表达式都为true,才为true。

分号类似或的关系,逗号类型与的关系

合法的关卡表达式是所有合法erlang表达式的一个子集。确保无副作用。关卡是模式匹配的一种扩展。

合法的关卡表达式

1.原子true

2.其他常量(各种数据结构和已绑定常量),在关卡表达式中为false

3.调用后面表1里的关卡判断函数和表2里的内置函数

4.数据结构比较

5.算术表达式

6.布尔表达式

7.短路布尔表达式

关卡示例

fun(X,Y) when is_integer(X), X>Y, X<6 -> ...

X为整数,大于Y,小于6

true关卡的作用

if
    Guard -> expressioons;
    Guard -> expressioons;
    Guard -> expressioons;
    ..
    true ->  expressioons
end.

case和if表达式

case expressions of
    pattern1 [when Guard] -> Expr_sql1;
    pattern2 [when Guard] -> Expr_sql2;
    ..
end.

if表达式

if
   Guard1->
       Expr_seq1;
   Guard2->
       Expr_seq2;
end.

(类似if elseif else)很多时候,if表达式的最后一个关卡是true,确保其他关卡都是失败时表达式最后部分会执行。

构建自然顺序的列表

some_funciton([H|T],..., result,...) ->
    H1 = ... H ...,
    some_function(T,...,[H1|result],...);
some_function([],...,result,...)->
    {...,result,...}.
  1. 总是向列表头添加元素

  2. 从输入列表的头部提取元素,然后把他们添加到输出列表的头部,形成的是与输入类表相反的输出类别

归集器

odds_and_evens2(L) ->
    odds_and_evens_acc(L,[],[]).

odds_and_evens_acc([H|T], Odds, Evens) ->
    case (H rem 2) of
        1 -> odds_and_evens_acc(T,[H|Odds],Evens);
        0 -> odds_and_evens_acc(T,Odds,[H|Evens])
    end;

odds_and_evens_acc([], Odds, Evens) ->
    {Odds, Evens}.

lib_misc:odds_and_evens2([1,2,3,4,5,6]).
{[5,3,1],[6,4,2]}
erlang9