Hatena::Grouperlang

msyktの日記

2013-12-14

3.4 fun

19:27

Erlangの環境を作り直した為に間が空いたが、再開。

funとは「無名の」関数だ。funには名前がないのでこう呼ばれる。

funから外のスコープの変数の値を参照できるか確認してみる。

-module(test34).
-compile(export_all).

double_plus(F) ->
    L = [1,2,3,4],
    lists:map(fun(X) -> X*2+F end, L).
masayuki@d7f347248f4e:~/work/erlang/3_4$ erl
Erlang R16B02 (erts-5.10.3) [source] [64-bit] [smp:2:2] [async-threads:10] [kernel-poll:false]

Eshell V5.10.3  (abort with ^G)
1> c(test34).
{ok,test34}
2> test34:double_plus(2).
[4,6,8,10]

funの外の変数の値を参照できることが分かる。

3.6 リスト内包表記

19:42

「A+B+CがN以下で、かつA*A+B*B=C*CであるようなA,B,Cをぞれぞれ1からNまでの間からすべて見つける」となる。

A、B、Cのすべての組み合わせを生成することが面白いのだが、どういう順序で生成されるのか気になったので、各値を生成するところだけ抜き出して確認してみた。

Eshell V5.10.3  (abort with ^G)
1> [{A, B, C} || A <- lists:seq(1,2), B <- lists:seq(1,2), C <- lists:seq(1,2)].
[{1,1,1},
 {1,1,2},
 {1,2,1},
 {1,2,2},
 {2,1,1},
 {2,1,2},
 {2,2,1},
 {2,2,2}]

C→B→Aの順なので、外側から生成されていくものらしい。

3.8 ガード

19:47

ガードのテスト項目の区切りに使われているカンマは「論理積」を意味する。

要はテスト項目が「A,B,C」であれば「A and B and C」ということだろう。

-module(test38).
-compile(export_all).

guard_test() ->
    L = {1,2,3,4,5,6},
    func(L).

func(L) when is_tuple(L), size(L) =:= 5, abs(element(3, L)) =:= 3 ->
    io:format("when is_tuple(L), size(L) =:= 5, abs(element(3, T)) =:= 3");
func(L) when is_tuple(L), size(L) =:= 6, abs(element(3, L)) =:= 3 ->
    io:format("when is_tuple(L), size(L) =:= 6, abs(element(3, T)) =:= 3");
func(L) ->
    io:format("others").
Eshell V5.10.3  (abort with ^G)
1> c(test38).
test38.erl:12: Warning: variable 'L' is unused
{ok,test38}
2> test38:guard_test().
when is_tuple(L), size(L) =:= 6, abs(element(3, T)) =:= 3ok

すべて一致した2番目の項目のみが実行されていることを確認した。

3.10 case式とif式

19:56

しかし、この定義はもう1つの(filter1という)関数を作ってfilter/2の引数をすべてその関数に渡すようにする必要があるので、かなり見苦しい。

filter1という関数名を使わず、filter1/4→filter/4にしたらどうだろう?

-module(test310).
-compile(export_all).

filter(P, [H|T]) ->
    filter(P(H), H, P, T);
filter(P, []) -> [];
filter(true, H, P, T) ->
    [H|filter(P, T)];
filter(false, H, P, T) ->
    filter(P, T).
Eshell V5.10.3  (abort with ^G)
1> c(test310).
test310.erl:7: head mismatch
error

コンパイルできなかった。アリティが違うと別関数なので;(セミコロン)で続けることはできなかった。filter/2とfilter/4で分けてみる。

-module(test310).
-compile(export_all).

filter(P, [H|T]) ->
    filter(P(H), H, P, T);
filter(P, []) -> [].

filter(true, H, P, T) ->
    [H|filter(P, T)];
filter(false, H, P, T) ->
    filter(P, T).
Eshell V5.10.3  (abort with ^G)
1> c(test310).
test310.erl:6: Warning: variable 'P' is unused
test310.erl:10: Warning: variable 'H' is unused
{ok,test310}
2> test310:filter(fun(X) -> X rem 2 =:= 0 end, [0,1,2,3,4,5]).
[0,2,4]

こちらは問題なく動く。別関数に渡すのが見苦しい、ということですかね。

4.3 try...catch

22:28

afterセクションのコードはtryセクションやcatchセクションの式のコードの直後に実行される。

-module(test43).
-compile(export_all).

func1() ->
    try nonthrow_func() of
        _ -> io:fwrite("a\n")
    catch
        _:_ -> io:fwrite("b\n") 
    after
        io:fwrite("c\n")
    end.

func2() ->
    try throw_func() of
        _ -> io:fwrite("a\n")
    catch
        _:_ -> io:fwrite("b\n")
    after
        io:fwrite("c\n")
    end.

throw_func() ->
    throw(a).

nonthrow_func() ->
    ok.
Eshell V5.10.3  (abort with ^G)
1> c(test43).
{ok,test43}
2> test43:func1().
a
c
ok
3> test43:func2().
b
c
ok

確かに。try...catch...afterの順で実行されている。

5.3 ビット構文

22:56

変数Mの16ビットメモリ領域に3つの変数X,Y,Zをパックしたいとしよう。Mの中でXは3ビット、Yは7ビット、Zは6ビットを占めるものとする。ほとんどの言語ではビットシフトとビットマスクを駆使した面倒な低レベル操作が必要だが、Erlangならば次のように書くだけでよい。

M = <<X:3, Y:7, Z:6>>

簡単だ!

これは面白い。試してみる。

Eshell V5.10.3  (abort with ^G)
1> X=5,Y=12,Z=34.
34
2> M = <<X:5, Y:5, Z:5>>.
<<43,2:7>>
3> <<Rx:5, Ry:5, Rz:5>> = M.
<<43,2:7>>
4> Rx.
5
5> Ry.
12
6> Rz.
2

あれ、Rzの値がおかしい…と思って良く見たら、2^5=32を超えた値を設定したからか。

7> N = <<X:5, Y:5, Z:6>>.   
<<"+\"">>
9> <<Sx:5, Sy:5, Sz:6>> = N.
<<"+\"">>
10> Sx.
5
11> Sy.
12
12> Sz.
34

これは便利だ。