「すごい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
Erlang World
ガード1 -> 処理1;
ガード2 -> 処理2;
true -> 処理3
end,
とのことなので、結局のところ、ガード節に適合しない関数は、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さん、ありがとうございます!
