再びHaskellでの実装を念頭に置いてRubyで実装したプログラムをHaskellで書き下すスタイルで勉強してみる。今回は円柱の体積の計算を通じて、Haskelのlet
とwhere
について見ていく。
いささか無理のある例ではあるが、半径と高さがわかっているたくさんの円柱の体積を求める必要があるとする。Rubyで実装すると以下のプログラムが1つの答えになる。
def cylinderVolumes(cylinders)
def volume(radius, height)
pi = 3.14
radius ** 2 * pi * height
end
cylinders.map{|cylinder|
r = cylinder[0]
h = cylinder[1]
volume(r, h)
}
end
p cylinderVolumes [[1.0, 1.0], [2.5, 1.0], [2.5, 2.5]]
Rubyにはタプルは存在しないので、2要素のリストを便宜的に円柱の半径と高さを保持した要素としよう。この要素のリストが体積を求めたい円柱の一覧である。
これをHaskellで実装する。Haskellでは局所関数や変数の定義にlet
やwhere
を用いる。今回は双方のパターンで実装することにする。
いずれの例も、円周率を示す局所変数pi
と、円柱の体積を計算する局所関数volume
を定義する。Haskellにはタプルが存在するので、円柱の半径と幅はタプルに格納し、計算を行うcylinderVolumes
関数はそのリストを受け取るようにする。
cylinderVolumes :: [(Double, Double)] -> [Double]
cylinderVolumes xs = [volume r h | (r, h) <- xs, let pi = 3.14 ; volume radius height = radius ^ 2 * pi * height]
cylinderVolumes :: [(Double, Double)] -> [Double]
cylinderVolumes xs = [volume r h | (r, h) <- xs]
where pi = 3.14
volume radius height = radius ^ 2 * pi * height
あとはmain
からcylinderVolumes
を呼び出してやれば、円柱の体積を求めることができる。GHCiからこれらのプログラムをロードして実行させてみよう。
*Main> cylinderVolume [(1.0, 1.0), (1.0, 2.5), (2.5, 2.5)]
[3.14,7.8500000000000005,49.0625]
さて、今回のテーマはlet
とwhere
だが、その違いはHaskell初心者にはなかなかに難しい。ポイントとしては以下のようである。
-
let
は式であり、where
は式ではない -
where
のスコープは局所的でなく関数定義のガードを跨いで使うことができる
“すごいHaskellたのしく学ぼう!”の3章には以下の記載がある。むむむむむ。結局は適材適所で使い分け?(逃げた)
はじめのうちは、
let
は束縛を先に書いて式を後に書くけどwhere
はその逆という違いしかないように思えるかも知れません。 本当の違いは、let
式はその名のとおり「式」で、where
節はそうじゃない、というところです。 (中略) つまり、let式は次のようにコード中のほとんどどんな場所でも使えるということです。 ghci > 4 * (let a = 9 in a + 1) + 2 42 (中略) また、関数の前ではなく後ろで部品を定義するのが好きだからwhere
を使うという人もいます。
オーム社
売り上げランキング: 126,678