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

これは便利だ。

2013-11-02

2.11 文字列(p.24)

00:42

厳密に言えば、Erlangには文字列は存在しない。文字列は実際には単なる整数のリストだ。

Erlangが変わってるな、と思う事の一つがこれ。文字列は存在しないけど、APIリファレンスを読むと、

atom_to_list(Atom) -> string()

となっていて、やっぱり文字列型ってあるのか?と思ってしまう。

このページに、

リストの値を表示するとき、そのリストに含まれるすべての整数が表示可能な文字を表していれば、シェルはリストを文字列として表示する。

とあるので、リストが文字を表す数値で構成されている場合は文字列型、そうでない場合はただのリストなのかな。

文字コードは、

文字列の各文字はLatin-1(ISO-8859-1)文字コードで表現される。

とあるので、Latin1…。

プログラミングErlang

プログラミングErlang

2013-10-31

2.9 タプル(p.20)

23:59

Cと違うのは、タプルの各フィールドには名前がない点だ。タプル自体は整数のペアを持つだけなので、タプルが何に使われているかを覚えておかないといけない。タプルが何に使われているかを思い出しやすくするため、タプルが表現するものを示すアトムを最初の要素に入れておくことが多い。

このCの構造体とタプルの比較の話はなるほどと思った。普段タプルがある言語を使わないので気にしたことが無かったが、最初の要素に識別子を入れるというのはそういう対比で説明できるのか。

複雑なタプルから値を取り出すには、タプルと同じ形(構造)で、タプルの取り出したい値に対応する箇所に未束縛変数を置いたパターンを書けばよい。

シンボル_は無名変数と呼ばれる。_は通常の変数とは異なり、1つのパターンの中で何度か使う場合でも同じ値に束縛されなくても良い。

「_」は無名変数と呼ぶのか。てっきりワイルドカードなのかと思ってた。

Eshell V5.10.1  (abort with ^G)
1> A = {foo, {bar, {hoge, hogehoge}}}.
{foo,{bar,{hoge,hogehoge}}}
2> {_, {_, {_, Deepest}}} = A.
{foo,{bar,{hoge,hogehoge}}}
3> Deepest.
hogehoge
プログラミングErlang

プログラミングErlang

2.10 リスト(p.22)

00:29

[...|T]構築子を使ってリストを作るときは、Tが必ずリストであるように気を付けなければならない。Tがリストならば、作ったリストは「正しい形式」になる。Tがリストではない場合、出来上がったリストを「不正な形式」であるという。ライブラリ関数のほとんどはリストが正しい形式であると仮定していて、不正なリストに対しては正しく機能しない。

リストはこの部分だけ気になった。

Eshell V5.10.1  (abort with ^G)
1> A = [1, 2].  
[1,2]
2> B = 3.
3
3> C = [0 | A].
[0,1,2]
4> D = [0 | B].
[0|3]
5> is_list(D).
true
6> [E | F] = D.
[0|3]
7> E.
0
8> F.
3

Tにリスト以外の値である「3」を指定してリストを構築してみたら、[0|3]という不思議なリストができてしまった。[H|T]のパターンマッチで値を取り出せるけど、Tがリストではないので、Tがリストだと仮定するとエラーになってしまうのだろうか。[...|T]で構築する際、Tがリストでなければエラーとすれば良いと思うのだけれども、敢えてリスト以外も許容するのに何か理由があるのかな。

プログラミングErlang

プログラミングErlang

2013-10-30

2.8 アトム(p.19)

01:13

アトムは小文字で始まり、その後に英数字、アンダースコア(_)、アットマーク(@)の列が続く。

Eshell V5.10.1  (abort with ^G)
1> is_atom(aB).
true
2> is_atom(Ab).
* 1: variable 'Ab' is unbound
3> is_atom(1a).
* 1: syntax error before: a
3> is_atom(_a).
* 1: variable '_a' is unbound
4> is_atom(@a).
* 1: syntax error before: '@'

英文字は全部小文字である必要があるのだと思っていたが、「aB」もアトムとして認識された。英文字の小文字で始まっていればアトムと考えて良さそう。

アトムは単一引用符(')で囲んで示すこともできる。引用符で囲む方法を使えば、大文字で始まるアトム(そのままでは変数として解釈されてしまう)や、英数字意外の文字を含むアトムを作れる。例えとしては、'Monday'、'Tuesday'、'+'、'*'、'an atom with space'など。引用符で囲む必要のないアトムを引用符で囲んでも構わない。'a'とaはまったく同じ意味になる。

Eshell V5.10.1  (abort with ^G)
1> is_atom('Monday').
true
2> is_atom('+').
true
3> is_atom(+).  
* 1: syntax error before: ')'
3> is_atom('an atom with space').
true
4> 'a' == a.
true

スペース含めても良いっていうのが面白い。'so tired'や'very sad'という文芸チックなアトムが書ける。

プログラミングErlang

プログラミングErlang

2013-10-28

2.7 浮動小数点(p.18)

02:34

「/」は常に浮動小数点を返す。そのため4/2は(シェルでは)2.0000に評価される。N div MとN rem Mは整数の除算と剰余に使われる。

Eshell V5.10.1  (abort with ^G)
1> 4/2.
2.0
2> 1/3.
0.3333333333333333
3> 2/3.
0.6666666666666666
4> 5/3.
1.6666666666666667
5> 4 div 2.
2
6> 1 div 3.
0
7> 5 div 3.
1
8> 5 rem 3.
2
9> 5 div 3.0.
** exception error: an error occurred when evaluating an arithmetic expression
     in operator  div/2
        called as 5 div 3.0
10> 5.0 div 3.
** exception error: an error occurred when evaluating an arithmetic expression
     in operator  div/2
        called as 5.0 div 3
11> 5 rem 3.0.
** exception error: an error occurred when evaluating an arithmetic expression
     in operator  rem/2
        called as 5 rem 3.0

有効桁数や丸め方(2/3の最後が7じゃない)とか細かいところで引用文と異なるが、基本的なルールは引用文にあるとおり。divやremに浮動小数点を指定すると例外が発生する。

プログラミングErlang

プログラミングErlang