第二节 模块与函数(上)
模块
模块是erlang的基本代码单元.模块保存在扩展名为.erl的文件里,而且必须先编译才能运行模块里面的代码.编译后的模块以.beam作为扩展名.
我们创建一个geometry.erl的文件
1 -module (geometry). 2 -export ([area/1]). 3 4 area({rectangle,Width,Height}) -> Width * Height; 5 area({square, Side}) -> Side * Side.
然后在shell中编译这个模块,并且运行它.
1> c(geometry).
{ok,geometry}
2> geometry:area({rectangle, 10, 5}).
50
3> geometry:area({square,4}).
16
第一行c(文件名)的作用是编译文件里面的代码,运行成功会返回{ok,xxxxxx}这样的结果.同时生成一个.beam的目标代码模块.
第二行我们通过文件名:方法(参数)的方式来调用模块中的函数.(请注意,需要给函数名附上模块名,这样才能准确标明想调用的是哪个函数).
下一步,我们试着在模块中添加一些简单的测试模块.把之前那个模块命名为geometry_test.erl
1 -module (geometry_test). 2 -export ([test/0,area/1]). 3 4 test() -> 5 12 = area({rectangle, 3, 4}), 6 144 = area({square, 12}), 7 tests_worked. 8 9 area({rectangle,Width,Height}) -> Width * Height; 10 area({square, Side}) -> Side * Side.
然后我们在shell中编译模块并且运行如果得到test_worked这样的字样就表示测试通过.
1> c(geometry_test).
{ok,geometry_test}
2> geometry_test:text().
** exception error: undefined function geometry_test:text/0
3> geometry_test:test().
tests_worked
如果在shell中输入错了函数名就会有错误提示.
我们给之前的函数扩展一下增加一个area({circle, Radius}) -> 3.14159 * Radius * Radius. 要添加的子句,顺序无关紧要.无论子句如何排列,程序都是一个意思,因为子句里面的各个模式是互斥的.这就让我们编写扩展程序简单了很多.
最后再看一下我们这个模块,我们会发现有逗号(,)分号(;)句号(.)这样的符号.
逗号(,)分隔函数调用,数据构造和模式中的参数.
分号(;)分隔子句.
句号(.)分隔函数整体.
fun:基本的抽象单元
erlang是一种函数式的编程语言.此外,函数式编程语言还表示函数可以被用作其他函数的的参数,也可以返回函数.操作其他函数的函数被称为高阶函数,而在erlang中用于代表函数的数据类型被称为fun.
1.对列表里面的每一个元素执行相同的操作.
2.创建自己的控制抽象.(erlang中没有for循环)
3.实现可重入解析代码,解析组合器,或惰性求值器等事物.
1> Double = fun(X) -> 2*X end.
#Fun<erl_eval.6.54118792>
2> Double(2).
4
上面就是在shell中定义一个fun的方法.
下面我们看看定义2个参数是什么样的.
3> Hypot = fun(X, Y) -> math:sqrt(X*X + Y*Y) end.
#Fun<erl_eval.12.54118792>
4> Hypot(3,4).
5.0
接下来我们以fun作为参数.标准库中lists模块导出了一些以fun作为参数的函数.他们之中最有用的就是lists:map(F,L).这个函数返回的是一个列表,它通过给列表L里的各个元素应用fun F生成.
5> L = [1,2,3,4].
[1,2,3,4]
6> lists:map(fun(X) -> 2*X end, L).
[2,4,6,8]
另外一个有用的函数是lists:filter(P, L),它返回一个新的列表,内含L中所有符合条件的元素(条件是对元素E而言P(E)为true).
Even = fun(X) -> (X rem 2) =:= 0 end.
定义一个函数Even(X),如果X是偶数就返回true. rem会计算出除以2后的余数,=:=用来测试是否相等.
然后我们用map和filter为参数.
10> lists:map(Even, [1,2,3,4,5,6,8]).
[false,true,false,true,false,true,true]
11> lists:filter(Even, [1,2,3,4,5,6,8]).
[2,4,6,8]
函数不仅可以使用fun作为参数,还能返回fun.
12> Fruit = [apple,pear,orange].
[apple,pear,orange]
13> MakeTest = fun(L) -> (fun(X) -> lists:member(X, L) end) end.
#Fun<erl_eval.6.54118792>
14> IsFruit = MakeTest(Fruit).
#Fun<erl_eval.6.54118792>
15> IsFruit(pear).
true
16> IsFruit(apple).
true
17> IsFruit(dog).
false
记住括号里面的东西就是返回值.
下面我们来实现一个自定义的控制抽象,不过到目前为止erlang中我们还没有看见if语句,switch语句,for语句或者while语句,然后这一切都是用模式匹配和高阶函数编写的.我们自己实现一个for结构模块.
1 -module (lib_misc). 2 -export ([for/3]). 3 4 for(Max, Max, F) -> [F(Max)]; 5 for(I, Max, F) -> [F(I) | for(I+1, Max, F)].
然后在shell上执行这个模块
1> lib_misc:for(1,10,fun(I) -> I end).
[1,2,3,4,5,6,7,8,9,10]
2> lib_misc:for(1,10,fun(I) -> I*I end).
[1,4,9,16,25,36,49,64,81,100]