「すごいErlangゆかいに学ぼう!」3章のラストと復習

すごいErlangゆかいに学ぼう!」の読書メモ。

今日は3章全体を読み返して復習しつつ、3章ラストの「3.5 どれがいいの?」についてです。

(復習)関数宣言、関数節、関数のヘッド

前回よく分からないと書いたけど、脚注を読んだら書いてあった。飛ばしてすいません。

こういうことね。

(復習&疑問)case式を使って書かれたコードの書き替え

本文p.42に、次のようなサンプルコードがある。

insert(X,[]) ->
    [X];
insert(X,Set) ->
    case lists:member(X,Set) of
        true -> Set;
        false -> [X|Set]
    end.

その後、p.43に次の記述がある。

ちょっと前に書いたinsert/2関数は、ほぼ間違いなくcase式を使うほうが、関数呼び出しをしてtrueまたはfalseの節を持たせるよりすっきりします。

(上のサンプルコードが、「ちょっと前に書いたinsert/2関数」にあたる)

ここに書かれている「(case式を使わない)関数呼び出しをする書き方」というのが分からない。

たとえば、ガードやifを使って書き直したものが、それなのだろうか?ご意見いただきました。追記2参照。

ガードを使う

まず、上のコードをガードを使って書き直してみる。

insertg(X,Set) when Set =:= [] ->
    [X];
insertg(X,Set) when lists:member(X,Set) =:= true ->
    Set;
insertg(X,Set) when lists:member(X,Set) =:= false ->
    [X|Set].

しかし、このコードは、3行目と5行目でコンパイルエラーになる。

illegal guard expression

これは、ガード節で使えない関数(lists:member)を書いているせいらしい。

元のコードをガードを使って書くことはできるんだろうか? どうすればいいんだろう?

ifを使う

次に、ifを使って書き直してみる。

inserti(X,Set) ->
    if  
        Set =:= [] -> [X];
        lists:member(X,Set) =:= true -> Set;
        true -> [X|Set]
    end.

これも、4行目でコンパイルエラーになる。

if文の書式

if
ガード1 -> 処理1;
ガード2 -> 処理2;
true -> 処理3
end,

Erlang World

とのことなので、結局のところ、ガード節に適合しない関数は、ifの中にも書けないという認識でいいのだろうか。

だとすれば、元のコードをifを使って書き直すと、どういうコードになるのだろう。

(復習&疑問)ifを使って書かれたコードの書き替え

p.40のサンプルコードから引用。

help_me(Animal) ->
    Talk = if
        Animal == cat -> "meow";
        Animal == beef -> "mooo";
        Animal == dog -> "bark";
        Animal == tree -> "bark";
        true -> "fgdadfgna"
        end,
    {Animal, "says " ++ Talk ++ "!"}.

このコードには次のような注釈がついている。

関数のヘッドでパターンマッチするほうが望ましい! あくまで例としてこのようなコードにしている

というわけで、関数のヘッドでパターンマッチするバージョンを書いてみる。

help_me_ex(cat) ->
    {cat, "says meow!"};
help_me_ex(beef) ->
    {beef, "says mooo!"};
help_me_ex(dog) ->
    {dog, "says bark!"};
help_me_ex(tree) ->
    {tree, "says bark!"};
help_me_ex(Animal = _) ->
    {Animal, "says fgdadfgna!"}.

こうかな? 実行結果は、元のコードと同じになる。

上のコードで共通部分、つまり、

 "says " ++ Talk ++ "!"

が、すごく重複しているように感じるのだけど、こういう書き方をするときには共通化しなくてOKということ?かな?

3.5 どれを使えばいいの?

p.43から。

if、case ... of、関数という3つのうち、どれを使うかは、わりと答えるのが難しい質問です。

ここで挙げられている3択のうちの「関数」というのが何のことなのかよく分からない。関数を使った書き方とは、p.42にある「『ビーチに行くべき時間か?』を摂氏、絶対温度、華氏の3つの温度表現で答えるという関数」のような「書き方」のことを指しているのか? しかし、beach関数ではcase ... ofも使っている。3択は難しいよねという話をしているのであれば、「関数を使った書き方」=beach関数ではないのかも。うーん。日本語難しい。ご指摘いただきました。追記1参照。

3つの使い分けの指針はまだよくわからない(そもそも3つ目が何なのかが分からないw)。でも、上の復習にあったように、ガード節に書けない条件を必要とするときにはcase式を使えばよさそうだということが、現時点では理解できた。

今日はここまで :P

追記

2014/7/17 追記1

文脈から察するに「if、case ... of、関数という3つのうちどれ」これは多分関数の head でパターンマッチするかどうかっていう意味のはず。

https://twitter.com/kuenishi/status/489581730135699456

kuenishiさん、ご指摘ありがとうございます!

2014/7/17 追記2

insert/2 の「(case式を使わない)関数呼び出しをする書き方」について、ぼくの思っている方法はこんなものです > https://gist.github.com/shino/0f512e42dc9a60cec066

https://twitter.com/itawasa/status/489618222929289216
insert(X,Set) ->
    insert(X, Set, lists:member(X, Set)).
 
insert(X, Set, true)  -> X;
insert(X, Set, false) -> [X | Set].

なるほど、lists:memberの結果を引数に取ることで、ガードを必要とせずにパターンマッチで書けますね。本文の「関数呼び出しをしてtrueまたはfalseの節を持たせる」の意味が分かりました。itawasaさん、ありがとうございます!