[雑談]
Q.いつ大人(オヤジ)になるか?
A.枕が臭くなったら
ってまだ、身なり次第では高校生でもいけるらしい。
[和訳]LYHGG4-3
http://learnyouahaskell.com/syntax-in-functions#where
Creative Commons
Where!?
前のsectionで、こんなふうにBMIを計算する関数を定義したよね。
bmiTell :: (RealFloat a) => a -> a -> String
bmiTell weight height
| weight / height ^ 2 <= 18.5 = “You’re underweight, you emo, you!”
| weight / height ^ 2 <= 25.0 = “You’re supposedly normal. Pffft, I bet you’re ugly!”
| weight / height ^ 2 <= 30.0 = “You’re fat! Lose some weight, fatty!”
| otherwise = “You’re a whale, congratulations!”
気付いた?3回も同じことを繰り返してるのを。これはお仕置きレベルだね。なんのためのプログラミングだよって(どこへ蹴り飛ばしているのやら)。同じexpressionを3回も繰り返すなら、一度に計算してから、計算結果を名前にbindするなり、expressionの代わりにその名前を使うなりした方が理想的なんだ。そうだね、このように関数を修正できるんだ。
bmiTell :: (RealFloat a) => a -> a -> String
bmiTell weight height
| bmi <= 18.5 = “You’re underweight, you emo, you!”
| bmi <= 25.0 = “You’re supposedly normal. Pffft, I bet you’re ugly!”
| bmi <= 30.0 = “You’re fat! Lose some weight, fatty!”
| otherwise = “You’re a whale, congratulations!”
where bmi = weight / height ^ 2
guradの後にwhere句をつけて(パイプがインデントされているのと同じ感じでwhere句もインデントするのがいいね)、nameやfunctionを定義するんだ。これで、nameがgurad全体に行き届いて、繰り返しを防ぐ効果をもたらしてくれるんだ。 もし、すこし違ったBMIの計算をしようと決めたら、すぐに関数の定義を変更したいよね。こんな感じでBMIを定義しておくと、プログラムの可読性が上がるし、bmiの変数が一度しか計算されていないから、プログラムが作り直しやすいよね。それじゃ、もう少しだけ沖にいこうか。関数をこうするんだ。
bmiTell :: (RealFloat a) => a -> a -> String
bmiTell weight height
| bmi <= skinny = “You’re underweight, you emo, you!”
| bmi <= normal = “You’re supposedly normal. Pffft, I bet you’re ugly!”
| bmi <= fat = “You’re fat! Lose some weight, fatty!”
| otherwise = “You’re a whale, congratulations!”
where bmi = weight / height ^ 2
skinny = 18.5
normal = 25.0
fat = 30.0
関数のwhereセクションで定義したnameは関数の中でしか効き目がないから、他の関数にまでに感染する恐れはないんだ。 すべてのnameが縦に並んでいるのに気付いたと思うんだけど、もうちょっとかっこよく並べないのはHaskellがそれらが同じblockの部品なのかがわからなくて、混乱するからなんだ。where bindingsは異なるパターンの関数bodyを共有しないので、もし、同じ(共有する)名前で一つの関数の複数のパターンにアクセスしたいなら、それをgloballyに定義しないといけないんだ。
where bindingsをpattern matchのように使えるんだ。whereセクションをこんな風に書き換えることができるね。
…
where bmi = weight / height ^ 2
(skinny, normal, fat) = (18.5, 25.0, 30.0)
それじゃあ、whereを使って、firstname とlastnameを受け取って、それらのイニシャルを返す関数を作ってみよう。
initials :: String -> String -> String
initials firstname lastname = [f] ++ “. ” ++ [l] ++ “.”
where (f:_) = firstname
(l:_) = lastname
関数のパラメタをpattern matchingにすることもできるんだけど(すると、コードが短くなるし、すっきりする)、where bindingsでも似たようなことができることを確認を込めてやりました。
where blockで定数の定義をしたんだけど、ここでも関数が定義できるんだ。健康的なプログラミングが習慣になるように、weight-heightペアのリストを受け取って、BMIsのリストを返す関数を作ってみよう。
calcBmis :: (RealFloat a) => [(a, a)] -> [a]
calcBmis xs = [bmi w h | (w, h) <- xs]
where bmi weight height = weight / height ^ 2
こうすると、コンパクト! この例題で関数にbmiを入れたかった理由は関数のパラメタからだと、ひとつのBMIしか計算できなかったんだけど、関数にペアのリストを渡すと、次から次へと異なるBMIが現れるのを説明したかったからなんだ。
where bindingsもネスト構造にできるね。次文省略: It’s a common idiom to make a function and define some helper function in its where clause and then to give those functions helper functions as well, each with its own where clause.
[和訳]LYHGG4-2
http://learnyouahaskell.com/syntax-in-functions#guards-guards
Creative Commons
Guards, guards!
patternsは値がある形式に則しているのかを確かめたり、それを分解してみたりする方法だったんだけど、guardsは値の特性が正しいかどうか(真偽)を検証する方法なんだ。なんだかif文みたいなもんに聞こえると思うんだけど、そんなに違わないし、ほとんど同なじ。ただ、guardsを使うとコードが読みやすいし、条件を書き下すときに、なんだかパターンを弄んでる気分なんだ。
guradsの構文を説明するかわりに、さっそく浸かってみようか。
君のBMIによって、異なる忠告をする関数でも作ってみよう。体重を身長の2乗で割った値が君のBMIだから。もし、BMIが18.5以下なら、やせすぎってみなされるんだ。18.5〜25ぐらいなら標準だね。25〜30ぐらいなら、少し太りぎみかな。30以上なら、肥満。これを関数にしてみよう(ただし、BMIの計算はしないよ、BMIの数値から、忠告するところまでね)。
bmiTell :: (RealFloat a) => a -> String
bmiTell bmi
| bmi <= 18.5 = “You’re underweight, you emo, you!”
| bmi <= 25.0 = “You’re supposedly normal. Pffft, I bet you’re ugly!”
| bmi <= 30.0 = “You’re fat! Lose some weight, fatty!”
| otherwise = “You’re a whale, congratulations!”
guradsは関数名とそのパラメタを|(パイプ)で守衛しているんだ※64。少しだけ右側にインデントして整列させるのが慣習なんだ。guardは基本的にbooleanのexpressionなんだ。評価がTrueなら、body部分の関数を使うんだ。逆にFalseなら、次の関門に以降するだけだよ。それの繰り返し。もし、値が24.3でこの関数を呼び出したら、まず、最初に18.5以下かどうか調べられるんだ。違ったなら、次のguardにってね。二番目のguardで25.0以下だと判別されると、門が開いて、文字列が飛び出てくるんだ。
これは手続き型言語におけるbig if elseツリーを思い出させるよね。ただ、こっちの方がかなり読みやすいと思うんだ。次文省略:While big if else trees are usually frowned upon, sometimes a problem is defined in such a discrete way that you can’t get around them. guradはこのif else ツリーの代替法なんだ※65。
最後の門番はえてしてotherwiseなんだ。otherwiseの定義はシンプルで、どんとこいのTrue and catches everythingなんだ。これはpatternsとよく似ているけど、patternsはinputがパターンを充たすかどうかで、guardはbooleanの条件を調べるだけなんだ。もし、すべての関門でFalseと評価されたら(もちろん、最後の門番がいないときを考えているよ)、関数の評価は次のpatternに投げ出されるんだ(ごむたいな)。patternとguardがいかに協調的に働くか。身元確認と持ち物検査が済まないと、出国できなくて、不法入国者として投獄されるかも※66。
もちろん、お連れがたくさんいる関数にもguradを使うこともできるんだ。ユーザにBMIを計算させる代わりに、ユーザのheightとweightを受け取って、それを計算させる関数に修正してみよう。
bmiTell :: (RealFloat a) => a -> a -> String
bmiTell weight height
| weight / height ^ 2 <= 18.5 = “You’re underweight, you emo, you!”
| weight / height ^ 2 <= 25.0 = “You’re supposedly normal. Pffft, I bet you’re ugly!”
| weight / height ^ 2 <= 30.0 = “You’re fat! Lose some weight, fatty!”
| otherwise = “You’re a whale, congratulations!”
どうかヤセでありますように…
ghci> bmiTell 85 1.90
“You’re supposedly normal. Pffft, I bet you’re ugly!”
やった、太ってなかったよ!でも、Haskellの野郎がお前はUglyだって。ちきしょー!!
最初のguardの前に関数名とパラメタの右隣に=がないことに注意してね。おそらく、ここに=を書いて、syntax errorを頻発することになると思うから。
もういっちょ例をみてみよう。そうだ、オリジナルのmax関数を作ってみよう。覚えてるかな?比較できるものを二つ受け取って、そのうち大きい方を返す関数だったよね。
max’ :: (Ord a) => a -> a -> a
max’ a b
| a > b = a
| otherwise = b
インラインでもguardを書けるんだ。関数は短くなるけど、とっても読みにくいんだ。でも、せっかくだからインラインで書いてみよう。
max’ :: (Ord a) => a -> a -> a
max’ a b | a > b = a | otherwise = b
うげ!まったくもって読みにくい。とっとと次にいこう。guardを使って、オリジナルのcompare関数を作ってみよう。
myCompare :: (Ord a) => a -> a -> Ordering
a `myCompare` b
| a > b = GT
| a == b = EQ
| otherwise = LT
ghci> 3 `myCompare` 2
GT
infix’ ’で関数を呼び出せるし、こっちの方が読みやすい時があるから、覚えといて。
- ※64 鉄格子
- ※65 これを比較するのはちときついでしょう。switch -caseでええやん
- ※66 If no suitable guards or patterns are found, an error is thrown.
[雑談]
オーシャンズを観てきました。
細かい音響とかよく拾っていて(本当?)、生物達の息遣いや動きがスクリーン越しで感じられました。少し時事色が強いですね。クモガニの集団行動には私も興味が湧きました。






コメントを書く