1. ホーム
  2. haskell

[解決済み] ハスケル Where vs. Let

2022-06-13 02:01:29

質問

私はHaskellの初心者ですが、以下の点で非常に混乱しています。 ここで vs. させる . どちらも似たような目的を提供しているようです。という比較をいくつか読んだことがあります。 とは vs. させる がありますが、それぞれをどのような時に使うのか判別するのに苦労しています。どなたか、文脈や、いつどちらかを使うべきかを示すいくつかの例を示していただけませんか?

Where vs. Let

A where 節は関数定義のレベルでのみ定義することができます。通常、これは let の定義と同じです。 唯一の違いは、ガードが使用されている場合です . のスコープは where 節のスコープは全てのガードに及びます。これに対して let 式のスコープは、もしあれば、現在の関数節とガードのみです。

Haskellチートシート

この Haskell Wiki は非常に詳しく、様々なケースを提供していますが、仮定的な例を使っています。初心者の方には説明が短すぎると思います。

Letの利点 :

f :: State s a
f = State $ \x -> y
   where y = ... x ...

コントロール.モナド.ステート

が動作しないのは、whereが というのは、f = にマッチするパターンを参照しており、x がないためです。 がスコープ内にないためです。これに対して、もし がletで始まっていたなら、問題ないでしょう。 はうまくいきません。

Haskell WikiのLetの利点について

f :: State s a
f = State $ \x ->
   let y = ... x ...
   in  y

どこのメリット :

f x
  | cond1 x   = a
  | cond2 x   = g a
  | otherwise = f (h x a)
  where
    a = w x

f x
  = let a = w x
    in case () of
        _ | cond1 x   = a
          | cond2 x   = g a
          | otherwise = f (h x a)

宣言と式の比較

Haskellのwikiによると、宣言は ここで 節は宣言的であるのに対して とする。 式は表現的です。スタイルは別として、どのようにパフォーマンスが異なるのでしょうか?

Declaration style                     | Expression-style
--------------------------------------+---------------------------------------------
where clause                          | let expression
arguments LHS:     f x = x*x          | Lambda abstraction: f = \x -> x*x
Pattern matching:  f [] = 0           | case expression:    f xs = case xs of [] -> 0
Guards:            f [x] | x>0 = 'a'  | if expression:      f [x] = if x>0 then 'a' else ...

  1. 最初の例では、なぜ を使うのでしょうか? はスコープ内なのに ここで はないのですか?
  2. を適用することは可能でしょうか? ここで を適用できますか?
  3. いくつかは、変数が実際の式を表す実際の例にこれを適用することができますか?
  4. それぞれを使用する際に従うべき一般的なルールはありますか?

アップデート

後でこのスレッドに立ち寄る人のために、私は最高の説明がここにあることを発見しました: " Haskell への優しい入門 "です。

表現してみましょう。

Haskellのlet式は ハスケルのlet式は、ネストされたバインディングのセットが必要なときに 必要なときに便利です。簡単な例として を考えてみましょう。

let y   = a*b
    f x = (x+y)/y
in f c + f d

let式で生成されるバインディングの集合は 式で作られる束縛のセットは相互に再帰的であり パターンバインディングは遅延パターンとして扱われます。 パターンとして扱われます(つまり,暗黙のうちに ~). 唯一許される宣言の種類は 型定義のみです。 関数バインディング、そしてパターン バインディングのみです。

WHERE句です。

をスコープするのが便利な場合があります。 をスコープするのが便利な場合があります。 方程式をスコープするのが便利です。 節が必要です。

f x y  |  y>z           =  ...
       |  y==z          =  ...
       |  y<z           =  ...
     where z = x*x

これはlet式ではできないことに注意してください。let式はそれを囲む式にのみスコープを持ちます。where句は,一連の式又はcase式のトップレベルにおいてのみ許される。let式における束縛の性質と制約は、where節においても同じである。これら2つの入れ子スコープは非常に似ているように見えますが、let式は式であるのに対し、where句はそうではないことを思い出してください。

どのように解決するのですか?

1: 例の問題

f :: State s a
f = State $ \x -> y
    where y = ... x ...

はパラメータ x . の中のものは where 節にあるものは、関数のパラメータのみを参照することができます。 f のパラメータ(何もない)と外部スコープにあるものだけを参照できます。

2:使用するには where を使うには、2つ目の名前付き関数 を受け取る x のように、パラメータとして指定します。

f = State f'
f' x = y
    where y = ... x ...

とかこんな感じ。

f = State f'
    where
    f' x = y
        where y = ... x ...

3: 以下、完全な例として ... 's:

module StateExample where

data State a s = State (s -> (a, s))

f1 :: State Int (Int, Int)
f1 = State $ \state@(a, b) ->
    let
        hypot = a^2 + b^2
        result = (hypot, state)
    in result

f2 :: State Int (Int, Int)
f2 = State f
    where
    f state@(a, b) = result
        where
        hypot = a^2 + b^2
        result = (hypot, state)

4: どのような場合に let または where は好みの問題です。私は let を使って計算を強調し(前面に移動させ) where でプログラムの流れを強調する(計算を後ろに移動させる)ことができます。