串行编程
程序1.1用于计算整数的阶乘:
程序1.1
- -module(math1).
- -export([factorial/1]).
- factorial(0) -> 1;
- factorial(N) -> N * factorial(N - 1).
函数可以通过shell程序进行交互式求值。 Shell会提示输入一个表达式,并计算和输出用户输入的任意表达式,例如:
- > math1:factorial(6).
- 720
- > math1:factorial(25).
- 15511210043330985984000000
以上的“>”代表 shell 提示符,该行的其他部分是用户输入的表达式。之后的行是表达式的求值结果。
factorial 的代码如何被编译并加载至 Erlang 系统中是一个实现相关的问题。 [1]
在我们的例子中,factorial函数定义了两个子句:第一个子句描述了计算factorial(0)的规则,第二个是计算factorial(N)的规则。当使用某个参数对factorial进行求值时,两个子句按照它们在模块中出现的次序被依次扫描,直到其中一个与调用相匹配。当发现一个匹配子句时,符号->右边的表达式将被求值,求值之前函数定义式中的变量将被代入右侧的表达式。
所有的 Erlang 函数都从属于某一特定模块。最简单的模块包含一个模块声明、导出声明,以及该模块导出的各个函数的实现代码。导出的函数可以从模块外部被调用。其他函数只能在模块内部使用。
程序1.2是该规则的一个示例。
程序1.2
- -module(math2).
- -export([double/1]).
- double(X) ->
- times(X, 2).
- times(X, N) ->
- X * N.
函数double/1可在模块外被求值[2],times/2则只能在模块内部使用,如:
- > math2:double(10).
- 20
- > math2:times(5, 2).
- ** undefined function: math2:times(5,2) **
在程序1.2中模块声明-module(math2)定义了该模块的名称,导出属性-export([double/1])表示本模块向外部导出具备一个参数的函数double。
函数调用可以嵌套:
- > math2:double(math2:double(2)).
- 8
Erlang 中的选择是通过模式匹配完成的。程序 1.3 给出一个示例:
程序1.3
- -module(math3).
- -export([area/1]).
- area({square, Side}) ->
- Side * Side;
- area({rectangle, X, Y}) ->
- X * Y;
- area({circle, Radius}) ->
- 3.14159 * Radius * Radius;
- area({triangle, A, B, C}) ->
- S = (A + B + C)/2,
- math:sqrt(S*(S-A)*(S-B)*(S-C)).
如我们所期望的,对math3:area({triangle,3,4,5})得到6.0000而math3:area({square,5})得到 25 。程序1.3 引入了几个新概念:
元组——用于替代复杂数据结构。我们可以用以下 shell 会话进行演示:
- > Thing = {triangle, 6, 7, 8}.{triangle, 6, 7, 8}> math3:area(Thing).20.3332
此处Thing被绑定到{triangle, 6, 7, 8}——我们将Thing称为尺寸为4的一个元组——它包含 4 个元素。第一个元素是原子式triangle,其余三个元素分别是整数6、7和8。
模式识别——用于在一个函数中进行子句选择。area/1被定义为包含4个子句。以math3:area({circle, 10})为例, 系统会尝试在area/1定义的子句中找出一个与{circle, 10}相符的匹配,之后将函数定义头部中出现的自由变量Radius绑定到调用中提供的值(在这个例子中是10)。
序列和临时变量——这二者是在area/1定义的最后一个子句中出现的。最后一个子句的主体是由两条以逗号分隔的语句组成的序列;序列中的语句将依次求值。函数子句的值被定义为语句序列中的最后一个语句的值。在序列中的第一个语句中,我们引入了一个临时变量S。