自作zipとlists:zip
今日は息抜きにちょっとErlang勉強.だんだんと再帰処理にも慣れて来たし,組み込み関数になければ自分で書けばいいか,という気分でけっこう楽しい.
僕は学生時代に情報工学科のLisp授業に潜って以来,Lispを何度も勉強して挫折を繰り返しているのだけれど結局,Lispで普通のプログラムを書くことができないでいる.Lispの勉強自体は他の言語を理解する上でとても役に立っているので,それ自体はあまり気にしていないのだけれど.(おかげで僕はけっこうなLispコンプレックス持ちになってしまった)
Erlangを書いていると,あぁそういうことか,Lispでもこう書けばよかったのか,とプログラミングのイメージが沸いてくる.
懐古話はさておき,いろんな言語でmap関数を使うたびに思うのだけれど,map関数が処理をするリストは基本的にひとつである.複数のリストを同時に処理したい場合には,後述するように工夫が必要となる.
Common Lispの場合,リストは複数あってもよい.(Emacs Lispではひとつだけ)
> (mapcar #'(lambda (x) (* x x)) '(1 2 3)) (2 4 6) > (mapcar #'(lambda (x y z) (* x y z)) '(1 2 3) '(4 5 6) '(7 8 9)) (28 80 162)
こういうときにLispのmapcarは便利だと思う.
Erlangでもご多分に漏れず,ひとつのリストしか処理できなかったので,自分で書くことにした.これから長い? Erlangライフのためにこの関数が自作ライブラリmylib.erlの最初の関数になった.
-module(mylib). -compile(export_all). % Zip function zip (_F, [], []) -> []; zip (_F, [], _) -> []; zip (_F, _, []) -> []; zip (F, [H1|T1], [H2|T2]) -> [F(H1, H2) | zip(F, T1, T2)].
Lispでは(cons (car lis) (func (cdr lis))と書くところ,引数でHead, Tailを受け取ってしまうので,こんなにさらっと書ける.(え,マクロを使えば解決するって?)
例えば,こうやって使う.同じ長さでなかった場合には尻をきってくれるLispと同じ仕様.
> mylib:zip(fun(X, Y) -> io:format("~p ~p~n", [X, Y]) end, [1,2,3], [2,3,4,5]). 1 2 2 3 3 4
さて,ここまでやったところでぱらぱらとErlang本を眺めてlists:zipなる関数を知った.
こんな風にふたつのリストを与えると,タプルのリストにして返してくれる.長さの違うリストは例外を返す仕様らしい.
1> lists:zip([1,2,3], [4,5,6]). [{1,4},{2,5},{3,6}] > lists:zip([1,2,3], [4,5,6,7]). 2> lists:zip([1,2,3], [4,5,6,7]). ** exception error: no function clause matching lists:zip([],[7]) in function lists:zip/2
なので,上記に書いた自作zipを使わずとも,lists:zipを使って書ける.
> map(fun({X,Y}) -> io:format("~p ~p~n", [X, Y]) end, lists:zip([1,2,3], [2,3,4,5])). 1 2 2 3 3 4
さようなら自作zip.君のことは忘れないよ.
Erlangには可変長引数という概念がないのか,こういった場合,io:formatの第3引数みたいにリストで受け取って中で工夫するしかないのかなぁ.
Erlang楽しいなぁ.
僕の場合は叶わなかった初恋 (Lisp) を今の恋 (Erlang) に重ねてるんだろうけど.