-module(tut6). -export([list_max/1]). list_max([Head|Rest]) -> list_max(Rest, Head). list_max([], Res) -> Res; list_max([Head|Rest], Result_so_far) when Head > Result_so_far -> list_max(Rest, Head); list_max([Head|Rest], Result_so_far) -> list_max(Rest, Result_so_far). 39> c(tut6). {ok,tut6} 40> tut6:list_max([1,2,3,4,5,7,4,3,2,1]). 7
首先注意到这里有两个相同名称的函数list_max。尽管这些函数有不同数量的参数。在Erlang中它们被处理为完全不同的函数。但我们需要根据name/arity来分辨不同的函数,name是函数的名称,arity是参数的个数,对于上面的例子分别是list_max/1和list_max/2。
上面的例子接受一个我们构造的列表,我们使用Result_so_far“搬移”一个列表中的值。list_max/1简单的假设列表中最大值是列表的头部元素,并给调用list_max/2来处理剩下的元素来与头部元素进行对比,在上面代码就将是list_max([2,3,4,5,7,4,3,2,1],1)。如果我们尝试使用list_max/1来处理一个空列表,我们将得到一个错误提示。注意这个Erlang体系不能够捕获这种类型的函数错误,但是会有其他的办法来处理,稍后会有详细的讨论。
在list_max/2中我们继续“走完”声誉的列表,当Head>Result_so_far条件满足的时候使用Result_so_far替代原先的Head的值。->前面的when是一个关键词,告诉我们如果条件为真则执行函数的这一部分,我们将这种测试过程称为界定(guard)。如果一个条件界定不为真(也就是说界定失败),我们尝试运行函数的另一部分。在这个例子中如果Head不是大于Result_so_far,也就是Head等于或者小于Resutl_so_far,所以我们就不需要在函数的另一部分再加入一个界定来判断执行条件了。
一些有用的界定符例如“<小于”,“>大于”,“==等于”,“>=不小于”,“<=不大于”,“/=不等于”。
改变上面的程序,使其变为寻找列表中最小值的函数,我们只需要将<替换为>符号就可以了。(除此之外似乎还应该将函数名称改为list_min更为恰当 :))
记得我曾经在较早前提起过变量只能在作用域内被赋值一次吗?在上面我们看到,Result_so_far被重复的赋值了很多次,这是合法的,因为每次我们调用list_max/2的时候系统都会新建一个作用域并且每个作用域内的变量都是完全不一样的。
另一种创建和赋值给变量的方法是使用操作符=。所以如果我写下M=5,一个叫做M的变量就会被创建然后被赋值为5.如果我又在同一个作用域中写M=6,我们将得到一个错误提示。在shell中尝试下面的代码:
41> M = 5. 5 42> M = 6. ** exited: {{badmatch,6},[{erl_eval,expr,3}]} ** 43> M = M + 1. ** exited: {{badmatch,6},[{erl_eval,expr,3}]} ** 44> N = M + 1. 6
匹配操作符在分离和创建新的变量方面有独特的作用:
45> {X, Y} = {paris, {f, 28}}. {paris,{f,28}} 46> X. paris 47> Y. {f,28}
这里我们的X得到了值pairs,Y得到了{f,28}。
当然,如果我们尝试对其他的城市重复上面的操作又不改变变量,那么会得到出错信息:
49> {X, Y} = {london, {f, 36}}.
** exited: {{badmatch,{london,{f,36}}},[{erl_eval,expr,3}]} **
变量能够用来提高程序的可阅读性,例如,上面的list_max/2函数,我们可以写:
list_max([Head|Rest], Result_so_far) when Head > Result_so_far -> New_result_far = Head, list_max(Rest, New_result_far);
这样的写法可能更加清晰一点。
| 第1页: Erlang编程入门之变量的匹配、限定和... |