循环对于list是一个相当重要的操作,下面我们来看一下lists:keyreplace的库函数是怎样完成的
628-spec keyreplace(Key, N, TupleList1, NewTuple) -> TupleList2 when
629 Key :: term(),
630 N :: pos_integer(),
631 TupleList1 :: [Tuple],
632 TupleList2 :: [Tuple],
633 NewTuple :: Tuple,
634 Tuple :: tuple().
635
636keyreplace(K, N, L, New) when is_integer(N), N > 0, is_tuple(New) ->
637 keyreplace3(K, N, L, New).
638
639keyreplace3(Key, Pos, [Tup|Tail], New) when element(Pos, Tup) == Key ->
640 [New|Tail];
641keyreplace3(Key, Pos, [H|T], New) ->
642 [H|keyreplace3(Key, Pos, T, New)];
643keyreplace3(_, _, [], _) -> [].
上面的-spec是说明的意思,讲述每一个参数的格式,
那么当我们格式不相符合的时候会出现什么样的内容我们可以测试一下
Eshell V5.9 (abort with ^G)
1> List = [{1,first},{2,second}].
[{1,first},{2,second}]
2> lists:keyreplace(1,1,List,1).
** exception error: no function clause matching
lists:keyreplace(1,1,[{1,first},{2,second}],1) (lists.erl, line 636)
3>
这里显示的是636行,但是相应的代码中636行是第一行我们的逻辑方法,那么我们在来试一试输入错误的List
ถ4> lists:keyreplace(1,1,{1,first},{1}).
** exception error: no function clause matching
lists:keyreplace3(1,1,{1,first},{1}) (lists.erl, line 639)
5>
这里我们看到的是639,那么是在list进行匹配的时候出的错误,这也就说明-spec以下的代码只是单纯的为我们介绍了一下这个方法的各个参数和形式,对于程序本身是没有任何影响的
言归正传
当某些库函数无法满足我们的程序的时候,或者我们想要新加一些自己的方法对列表进行处理的时候该怎么做,erlang是没有for这个概念的,但是对于递归方法缺极大的补充了这个遗憾,搭配erlang的匹配模式,那么完成for的这种操作就相当简单了
[H|T] = List
这个代码是我们经常能在各类书籍中看到的,很好理解Head,Tail,第一个元素作为头,剩下的元素作为尾那么我们就可以提取出所需要的内容,我们尝试重写lists:foldl(Fun, Acc0, List
)方法
先看帮助文档中源foldl实现了什么功能
lists:foldl(fun(X, Sum) ->
X + Sum
end, 0, [1,2,3,4,5]).
15 > lists:foldl(fun(X, Prod) ->
X * Prod end, 1, [1,2,3,4,5]).
120
这个方法循环了列表每一个元素,并设置了一个Acc0的初始变量,对每一个元素进行了处理以后要返回一个新的值,将新的返回值作为下一个元素的初始Acc0传入依次循环最后得到结果(注意这个Acc0可用可不用)
-module(test).
-export([
foldl/3
]).
foldl(Fun, Acc0, List) when List =/= [] ->
[Head|Tail] = List,
NewAcc0 = Fun(Head, Acc0),
foldl(Fun, NewAcc0, Tail);
foldl(Fun, Accu, []) when is_function(Fun, 2) ->
Accu.
我们做一下测试
E:\>erl -make test.erl
Recompile: test
E:\>erl
Eshell V5.9 (abort with ^G)
1> test:foldl(fun(X, Sum) -> X + Sum end,0,[1,2,3]).
6
2>
,结果正确了,那么我们再看一下库函数的代码
foldl(F, Accu, [Hd|Tail]) ->
foldl(F, F(Hd, Accu), Tail);
foldl(F, Accu, []) when is_function(F, 2) -> Accu.
短短的三行代码,就解决了,而且简洁,代码优化做的真是perfect!!!
那么解决一下这个疑问[Hd|Tail] = []?难道不会匹配吗!测试一下
2> [Head|Tail] = [].
** exception error: no match of right hand side value []
其实结果很明显,即便不用测试也知道,但是心中有疑问测试一下又不用多长时间还能加深印象,何乐而不为呢
2014 3 18补充
when是erlang断言中的一种,算是一种过滤条件,当我们写循环方法时,为了代码的可读性时when是一个不错的选择,case虽然也可以在循环中起到判断作用,但是相对于when无法做到化繁就简