hello erlang, part 4

| Comments

练习重点

列表解析和高阶函数是erlang的基本用法之一,熟练掌握还是很有必要的。 作为一个阶段的学习总结,按照书上的一个练习题,尝试实现列表模块中的一些方法。

后续学习计划

再找时间总结一下位匹配用法,然后就开始客户端服务器编程模型, 网络和分布式方面的内容。

相关内容虽然都看过一下,但是没有敲过代码,感觉心里没底。还是那个硬道理:纸上得来终觉浅呀。

简单的测试用例

作为另外一个章节的内容,单元测试。我顺便实践一下erlang的单元测试框架eunit, 里边使用了宏来作为assert语句(使用eclipse插件可以有很好的提示), 总的来说,xunit的基本风格都差不多,注意import eunit的包,方法名采用_test结束。 执行的时候调用模块的test方法就可以了。我简单写了一些测试用例:

-module(myliststest).
-include_lib("eunit/include/eunit.hrl").
-import_all(mylists).

%% EUnit TestCase

%% all/2
all_empty_test() ->
  ?assert(mylists:all(fun(E) -> E > 10 end, [])).
all_test() ->
  ?assertNot(mylists:all(fun(E) -> E > 10 end, [11,12,10])).

%% any/2
any_empty_test() ->
  ?assertNot(mylists:any(fun(E) -> E > 10 end, [])).
any_test() ->
  ?assert(mylists:any(fun(E) -> E > 10 end, [11,12,10])).

%% append/1
append_1_test() ->
  ?assertEqual([1,2,3,a,b,4,5,6], mylists:append([[1, 2, 3], [a, b], [4, 5, 6]])).

%% append/2
append_2_test() ->
  ?assertEqual("abcdef", mylists:append("abc", "def")).

%% delete/2
delete_test() ->
  ?assertEqual([1,2,4], mylists:delete(3, [1,2,3,4])).

%% dropwhile/2
dropwhile_test() ->
  ?assertEqual([1,2], mylists:dropwhile(fun(E) -> E >= 3 end, [1,2,3,4])).

%% duplicate/2
duplicate_test() ->
  ?assertEqual([x, x], mylists:duplicate(2, x)).

%% filter/2
filter_test() ->
  ?assertEqual([3,4], mylists:filter(fun(E) -> E >= 3 end, [1,2,3,4])).

%% reverse/1
reverse_test() ->
  ?assertEqual([4,3,2,1], mylists:reverse([1,2,3,4])).

%% min/1
min_test() ->
  ?assertEqual(2, mylists:min([3,4,2,5])).

%% zip/2
zip_2_test() ->
  ?assertEqual([{1,3},{2,4}], mylists:zip([1,2], [3,4,5])).

%% zip/3
zip_3_test() ->
  ?assertEqual([{1,3,5},{2,4,7}], mylists:zip3([1,2], [3,4,5], [5,7])).

%% zipwith/3
zipwith_test() ->
  ?assertEqual([5,7,9], mylists:zipwith(fun(X, Y) -> X+Y end, [1,2,3], [4,5,6])).

%% zipwith/4
zipwith3_test() ->
  ?assertEqual([[a,x,1],[b,y,2],[c,z,3]], mylists:zipwith3(fun(X, Y, Z) -> [X,Y,Z] end, [a,b,c], [x,y,z], [1,2,3])).

示例代码

根据上面的测试用例,我写了实现代码,如下所示。注意的时候,我偷懒使用了export_all, 但是min/2好像默认有一个已经被引入了,所以需要去除这个模块。就export_all本身来说, 也不是推荐方式,还是应该严格区分public api和local api。

-module(mylists).
-compile(export_all).
-compile({no_auto_import,[min/2]}).

%% all/2
all(_P, []) -> 
  true;
all(P, [H|T]) ->
  case P(H) of
	true -> all(P, T);
	false -> false
  end.

%% any/2
any(_P, []) ->
  false;
any(P, [H|T]) ->
  case P(H) of
	true -> true;
	false -> any(P, T)
  end.

%% append/1
append(LL) ->
  [X || L <- LL, X <- L].

%% append/2
append(LA, LB) ->
  LA ++ LB.

%% delete/2
delete(E, L) ->
  [X || X <- L, E /= X].

%% dropwhile/2
dropwhile(P, L) ->
  [X || X <- L, not P(X)].

%% duplicate/2
duplicate(N, E) ->
  duplicate(N, E, []).
duplicate(0, _E, L) ->
  L;
duplicate(N, E, L) when N > 0 ->
  duplicate(N-1, E, [E|L]).

%% filter/2
filter(P, L) ->
  [X || X <- L, P(X)].

%% reverse/1
reverse(L) ->
  reverse(L, []).
reverse([], L) ->
  L;
reverse([H|T], L) ->
  reverse(T, [H|L]).

%% min/1
min([H|T]) ->
  min(T, H).
min([], M) ->
  M;
min([H|T], M) ->
  if
    H < M -> min(T, H);
    true -> min(T, M)
  end.

%% zip/2
zip(LA, LB) ->
  reverse(zip(LA, LB, [])).
zip([], _LB, L) ->
  L;
zip(_LA, [], L) ->
  L;
zip([HA|TA], [HB|TB], L) ->
  zip(TA, TB, [{HA,HB}|L]).

%% zip/3
zip3(LA, LB, LC) ->
  reverse(zip3(LA, LB, LC, [])).
zip3([], _LB, _LC, L) ->
  L;
zip3(_LA, [], _LC, L) ->
  L;
zip3(_LA, _LB, [], L) ->
  L;
zip3([HA|TA], [HB|TB], [HC|TC], L) ->
  zip3(TA, TB, TC, [{HA,HB,HC}|L]).

%% zipwith/3
zipwith(P, LA, LB) ->
  [P(X,Y) || {X,Y} <- zip(LA, LB)].

%% zipwith/4
zipwith3(P, LA, LB, LC) ->
  [P(X,Y,Z) || {X,Y,Z} <- zip3(LA, LB, LC)].

Comments