<<Programming Erlang >>读书笔记---三

列表解析:
公式: [X || X <- L].
列表解析的功能可以通过多种实战来了解.

1> L = [apple,orange,banana,cherry].
[apple,orange,banana,cherry]
3> [{X,L}|| X <- L].
[{apple,[apple,orange,banana,cherry]},
{orange,[apple,orange,banana,cherry]},
{banana,[apple,orange,banana,cherry]},
{cherry,[apple,orange,banana,cherry]}]
4> [{X,[{Y}||Y <- L]}|| X <- L].
[{apple,[{apple},{orange},{banana},{cherry}]},
{orange,[{apple},{orange},{banana},{cherry}]},
{banana,[{apple},{orange},{banana},{cherry}]},
{cherry,[{apple},{orange},{banana},{cherry}]}]
6> [{X,lists:member(X,L)}|| X <- L].
[{apple,true},{orange,true},{banana,true},{cherry,true}]
7> [{X,lists:member(X,L),[is_atom(X),is_pid(X)]}|| X <- L].
[{apple,true,[true,false]},
{orange,true,[true,false]},
{banana,true,[true,false]},
{cherry,true,[true,false]}]

列表解析的作用域是列表,元组肯定不行,如下:
8> A = {a,b,c,d}.
{a,b,c,d}
9> [[X,111]||X <-A].
** exception error: bad generator {a,b,c,d}
10> [[X,111]||X <-L].
[[apple,111],[orange,111],[banana,111],[cherry,111]]

列表解析中,后面的部分(X <- L),是用X匹配L中每一个元素的部分,
需要非常注意的是,如果X匹配L中某一元素不成功,
Erlang不会抛出异常,而是忽略该元素,请看如下试验:
11> f().
ok
12> L.
* 1: variable 'L' is unbound
13> L = [{a,b,c},{d,e},{f,g,h}].
[{a,b,c},{d,e},{f,g,h}]
14> [X || {_,X,_} <- L].
[b,g]
{_,X,_} = {d,e}.%%这里肯定无法匹配,但Erlang没报错,只是忽略该元素在列表解析中.

由上面的那个试验,我们将引入一个很重要的概念,限定词(qualifier).
在列表中,限定词可以是一个生成器或者是一个过滤器,或者同时都是!
是生成器:P <- L,L的value必须是个列表!
是过滤器:限定词的value必须是true或者false.
同时都是: {_,X,_} <- [{a,b,c},{d,e},{f,g,h}].

我们最早的时候讲匹配模式的时候就说过,Erlang在设计匹配时,如果匹配不成功,则会抛出异常,而不是返回false.
上面那个试验在匹配不成功时,并没有抛出异常,但忽略了该元素,
所以<<Programming Erlang>>书中39页所说过滤器的值必须是true或者false是不对的,也可以抛出匹配不成功的异常,这相当于false.

试验一下生成器和过滤器:
17> [X|| X <- [1,2,3,4,5,6,7,8,9],X rem 2 =:= 0].
[2,4,6,8]
20> [X|| X <- [1,2,3,4,5,6,7,8,9],X rem 2 =:= 0,false].
[]
在第39页有一个快速排序的代码,该段代码利用Erlang列表解析的特性,通过生成器遍历,利用过滤器比较大小,再利用++操作符将列表元素串起来.

++操作符:
23> [1,2,3]++[4,5]++[6,7,8,9].
[1,2,3,4,5,6,7,8,9]

Erlang虽然是面向函数的编程语言,不同于以往的面向过程编程,
但在写代码的时候同样要注意代码的顺序,尤其是遇到哪个函数子句该先匹配,哪个该后匹配的时候.幸好,Erlang在编译时有提示.
试验如下:
seq(X,Y) -> [X|seq(X+1,Y)];
seq(Y,Y) -> [Y].

27> c(test1.erl).
./test1.erl:26: Warning: this clause cannot match because a previous clause at line 25 always matches
{ok,test1}

更改一下顺序:
seq(Y,Y) -> [Y];
seq(X,Y) -> [X|seq(X+1,Y)].

29> c(test1.erl).
{ok,test1}
30> test1:seq(10,20).
[10,11,12,13,14,15,16,17,18,19,20]

没任何问题了.

列表解析这里真得还有很多东西值得讲的,如果基本功不扎实,以后对语言深层次的理解就会遇到很大阻碍,所以让我们多试验几次.

我在timer模块找到一个函数:
tc(Module,Function,Arguments) -> {Time,Value}
我将利用者个函数计算一个函数执行的时间.

上面我们提到过列表解析中可以存在生成器,
你有没有想过在一个列表解析中使用多个生成器?
这肯定是可以的,只是这样写起来仿佛很潇洒,
但事实上遍历生成器,然后再通过过滤器,
执行的速度很多时候取决于生成器的长度,和过滤器的复杂度.
执行速度到底有多快呢?
我们用tc来试试.

现在我写了两个多生成器的函数,其中一个还带有过滤器,我门来看一下在不同生成器列表长度的情况,函数执行时间的问题.

maker(L) -> [{X,Y,Z}||X<-L,Y<-L,Z<-L].
maker_fie(L) -> [{X,Y,Z}||X<-L,Y<-L,Z<-L,X rem 2 =:= 0,Y rem 2 =:= 0,Z rem 2 =:= 0].

4> timer:tc(test1,maker_fie,test1:seq(1,1000)).
{78,
{'EXIT',{undef,[{test1,maker_fie,
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19|...]},
{timer,tc,3},
{erl_eval,do_apply,5},
{shell,exprs,6},
{shell,eval_exprs,6},
{shell,eval_loop,3}]}}}
其实在跑1000长度的列表时,Erlang的shell要执行很长很长时间的,我都没有等到它执行完.但用tc却看不出这个现象,仿佛时间很短,我还要研究下tc到底怎么用.

现在不管tc的问题了,我们自己写个benchmark的函数.
benchmark({Mo,Fn,Args}) ->
{_D1,T1} = erlang:localtime(),
Rt = Mo:Fn(Args),
{_D2,T2} = erlang:localtime(),
{timer:now_diff(T2,T1),Rt}.

这个函数可以很好的工作.
我做了三个试验,
一个是maker([长度100]).
{_D1,T1} = test1:benchmark({test1,maker,test1:seq(1,100)}).
一个是maker([长度200]).
{_D1,T2} = test1:benchmark({test1,maker,test1:seq(1,200)}).
另一个是maker([长度300]).
{_D2,T3} = test1:benchmark({test1,maker,test1:seq(1,300)}).
结果:
T1耗时1秒.
T2耗时10秒,
T3耗时32秒,且随后内存溢了.

由于多生成器的时候,Erlang会遍历每一种组合,这就会占用大量内存,设计程序的时候一定要注意,设计完成后一定要做性能测试!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值