【Erlang】Erlang入门(二)

1.模块

模块文件通常存放在以.erl为扩展名的文件中,编译成功后的文件扩展名为.beam。

2.函数

函数由多个子句构成,子句间以分号间隔,最后一条子句以句点作为结束符。

如:getArea.erl文件代码如下,

-module(getArea).
-export([area/1]). %Name/N,表示一个带有N个参数的名为Name的函数
area({rectangle, Width, Height}) -> Width * Height;
area({circle, R}) -> 3.14159 * R * R.

输入c(getArea). 即可编译getArea.erl文件,成功返回{ok, getArea},通过getArea:area({circle, 5}). 即可计算圆的面积。

注意:

        a.当调用函数时,对其调用参数的匹配过程从第一个子句开始依次向下执行;
                   b.函数不能处理模式匹配失败的情形,此时程序失败后会抛出一个运行时错误。

3.目录相关

pwd(). %打印当前目录
cd("/"). %切换到/目录
ls(). %查看当前目录下的内容

4.计算商品总价

shopTotal.erl代码如下:

-module(shopTotal).
-export([total/1]).
total([{What, N} | T]) -> shop:cost(What) * N + total(T);
total([]) -> 0.

5.fun

fun就是匿名函数,如: Double = fun(X) -> 2 * X end. erlang shell会打印#Fun<...>,这里的...是一些奇怪的数字。输入Double(4).即可调用该函数。

6.高阶函数

能够返回fun或者接收fun作为参数的函数,都被称为高阶函数。

7.以fun为参数的函数

list是标准库中的一个模块,从中导出的很多函数都是以fun作为参数的,其中最有用的是lists:map(F,L)。这个函数将fun F应用到列表L的每一个元素上,并且返回一个新的列表。

如,L = [1,2,3,4]. Double = fun(X) -> 2 * X end. lists:map(Double, L).可以得到[2,4,6,8].

另一个常用的函数是lists:filter(P,L). 它返回一个新列表,新列表由列表L中每一个满足P(E)为true的元素组成。比如,定义一个函数Even(X),当X为奇数时返回true。

Even = fun(X) -> (X rem 2) =:= 0 end.

其中,X rem 2是X对2取余,而=:=是一个恒等测试符号。

接下来测试Even分别作为map和filter的参数时的结果。

lists:map(Even, [1,2,3,4,5,6,8]).      %[false,true,false,true,false,true,true]
lists:filter(Even, [1,2,3,4,5,6,8]).   %[2,4,6,8]

8.返回fun的参数

定义一个函数MakeTest(L),将这个列表(L)转换为一个测试函数,这个测试函数将会检查它所传入的参数是否为列表L中的成员。比如,

MakeTest = fun(L) -> (fun(X) -> lists:member(X, L) end) end.   %->后面的括号中的东西就是返回值
Fruit = [apple, pear, orange].
IsFruit = MakeTest(Fruit).
IsFruit(pear).

MakeTest(Fruit)并非计算一个值然后返回它,而是会返回一个函数fun(X) -> lists:member(X, Fruit),这个函数只有在被调用的时候IsFruit(pear)才会计算具体的值。

如果X是L的成员,那么函数lists:member(X,L)将返回true,反之则返回false。

通常返回fun的参数都不容易调试,但这一技术可以用来解决诸如延迟求值、可重入的解析器、解析组合子等问题,因为这些问题本身就是返回解析器的函数。

9.实现for循环

forTest.erl

-module(forTest).
-export([for/3]).
for(Max, Max, F) -> [F(Max)];
for(I, Max, F) -> [F(I)|for(I+1, Max, F)].

erlang shell中输入c(forTest).后,再次输入forTest:for(1,5,fun(I) -> I end).即可产生[1,2,3,4,5]。

10.列表处理

mylists.erl计算列表中所有元素之和

-module(mylists).
-export([sum/1]).
sum([H|T]) -> H + sum(T);
sum([]) -> 0.

c(mylists).编译成功后,输入L = [1,3,4]. 接着输入mylists:sum(L). 可以得到结果8。

接下来让我们跟踪下具体的执行情况:

sum([1,3,4]) = 1 + sum([3,4]) = 1 + 3 + sum([4]) = 1 + 3 + 4 + sum([]) = 1+3+4+0 = 8

最后实现一个map/2

-module(mylists).
-export([map/2]).
map(_, []) -> [];
map(F, [H|T]) -> [F(H)|map(F, T)].

c(mylists).编译成功后,输入L = [1,2,3,4]. 接着输入mylists:map(fun(X) -> 2 * X end, L).即可得到[2,4,6,8,10]。

重写total函数(shopFinal.erl)

-module(shopFinal).
-export(total/1).               %意味着函数total/1能够在模块shopFinal之外调用
-import(lists, [map/2, sum/1]).	%意味着函数map/2是从lists模块中导入的,可以用map(Fun, ...)
total(L) -> sum(map(fun({What, N}) -> shop:cost(What) * N end, L)).

11.列表解析

列表解析是一种无须使用fun、map或者filter来创建列表的表达式。常见形式为:[X || Qualifier1, Qualifier2,...]

X可以是任意一个表达式,每个限定词可以是一个生成器或者一个过滤器。

生成器通常写为Pattern<-ListExpr,其中ListExpr必须是一个对列表项求值的表达式。

过滤器可以是一个谓词(返回true或者false的函数),也可以是一个布尔表达式。

比如之前将列表当中的元素加倍的例子,使用列表解析的方式,可以这样写:

[2 * X || X <- L].

记号[F(X) || X <- L]代表"由F(X)组成的列表,其中X取值于列表L"。因此上述表达式意味着列表L中每一个元素X乘上2以后的列表。

再来看下元组中的列表解析,

Buy = [{apple, 3}, {pear, 2}].
[{Name, 2 * Num} || {Name, Num} <- Buy].

注意,记号(||)右边的元组{Name, Num}是用于匹配列表Buy中每个元素的模式,左边的元组{Name, 2*Num}是个构造器。

那么total函数还可以这样写:

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

列表解析能明显地缩短代码,比如map可以这样写:

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

12.实现快速排序

qsortTest.erl

-module(qsortTest).
-export([qsort/1]).
qsort([]) -> [];
qsort([Pivot|T]) -> qsort([X || X <- T, X < Pivot])
		    ++ [Pivot] ++
		    qsort([X || X <- T, X >= Pivot]).

13.实现字符串所有可能的排列

permsTest.erl

-module(permsTest).
-export([perms/1]).
perms([]) -> [[]];
perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])].

14.断言

断言(guard)是一种用于强化模式匹配的结构。如实现一个比较大小的函数max(X,Y),那么使用断言可以这样实现:

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

如果第一个子句不匹配,那么尝试匹配第二个子句,此时Y必然大于等于X。

15.断言序列

断言序列可以是单个断言,也可以是一系列用分号分开的断言集合,只要任意一个断言为true,那么整个断言序列就为true。

断言也可以是一系列用逗号分开的断言集合,只有所有的断言都为true,整个断言序列才是true。

你可能感兴趣的:(Erlang)