基本的な構文
7.7 定義と束縛
let式とwhere節
確かに似てます。違いの説明がなければ、理解出来ませんでした。
第一の違い、letは式で、whereは節。
式は値を持つけど、whereは値を持たない。
letは式なので、let式をそのまま関数の定義や値として扱うことが出来るけど、
where節はあくまで、他の式をサポートする立場。
使われ方もそんな感じで分類されている。
こんな感じ。
- myLet.hs
main = do print $ myLet 10 myLet :: Int -> Int myLet x = let xs = [0..x] in myWhere xs where myWhere [] = 0 myWhere (x:xs) = x + myWhere xs
let式は関数内で変数を導入するために使われる。
C言語でいうと(constな)ローカル変数のような感じ。
where節は関数スコープでのみ有効な関数を定義するために使われる。
C言語では、関数スコープで定義されたマクロみたいなものかなぁ…。こんな感じ。
- myLet.c
int myLet(int x) { int* xs; int i, n; #define myWhere(nn, _xs, nm) \ do { \ int ii; \ nn = 0; \ for(ii = 0; ii < nm; ii ++) \ nn += _xs[ii]; \ } while(0) xs = (int*)malloc(sinzeof(int) * (x + 1)); if(0 == xs) { return 0; } for(i = 0; i < x + 1; i ++) { *(xs + i) = i; } myWhere(n, xs, x + 1); free(xs); return n; }
何だかすごいことになってしまった(汗)。
ビルド出来なければすいません。ふいんきだけでも…。
変数のシャドウイングについて
なるほど、let式で導入する変数の編数名が同じだった時は後勝ちなんですね。
たとえばこんな感じ。
main = do print $ myShadow 10 myShadow :: Int -> Int myShadow x = let cub = pow * x pow = x * x in let cub = x in cub * pow
% ghc -W -o myShadow myShadow.hs myShadow.hs:4:17: Warning: Defined but not used: `cub' % ./myShadow 1000
あれれ、シャドウイングが起こっている変数(cub)が使われていないと言われました。
結果を見ると意図通りですね。
でも同じ変数名を導入するのはあんまりよくないですよね。出来れば使わない方がよさげです。
定義中での相互参照
これ面白いですね。Haskellらしいというか。
C言語でもこんな感じでよくやるんですが、
int function(int x) { int xx = x * x; int xxx = x * xx; int xxxx = x * xxx; // … }
C言語ではあんまりよい作法とは言えませんけどね。
でもHaskellでは順番は関係ない。さすが遅延評価!
もちろんC言語では順番が超重要です。
int function(int x) { int xxxx = x * xxx; int xxx = x * xx; int xx = x * x; // … }
なんて書いたら即エラーです。でもHaskellでは
- myFunc.hs
main = do print $ myFunc 10 myFunc :: Int -> Int myFunc x = let xxxx = x * xxx xxx = x * xx xx = x * x in xxxx * xxx * xx * x
でも問題なし♪
遅延評価ってとても人間的ですよね。
人間なら上を参照してようが、下を参照してようが関係ないですから。