137

私は Haskell を初めて使用し、WhereLetで非常に混乱しています。どちらも同様の目的を提供しているようです。WhereLetの比較をいくつか読んだことがありますが、それぞれをいつ使用するかを判断するのに苦労しています。誰かがコンテキストや、どちらを使用するかを示すいくつかの例を提供してもらえますか?

どことレット

where句は、関数定義のレベルでのみ定義できます。通常、それはlet定義の範囲と同じです。唯一の違いは、ガードが使用されている場合です。where句の範囲は、すべてのガードに及びます。対照的に、let式のスコープは、現在の関数句とガード (存在する場合) のみです。

Haskellチートシート

Haskell Wikiは非常に詳細で、さまざまなケースを提供していますが、仮説的な例を使用しています。初心者には説明が短すぎると思います。

Let の利点:

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

Control.Monad.State

where は f = に一致するパターンを参照し、x が範囲内にないため、機能しません。対照的に、let で開始した場合は、問題はありません。

Let の利点に関する Haskell Wiki

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 では、 Where句は宣言的であり、Let式は表現的であると述べています。スタイルは別として、彼らはどのように異なるパフォーマンスを発揮しますか?

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. 最初の例では、Letがスコープ内にあるのにWhereがそうでないのはなぜですか?
  2. 最初の例にWhereを適用することは可能ですか?
  3. 変数が実際の式を表す実際の例にこれを適用できる人はいますか?
  4. それぞれをいつ使用するかに従う一般的な経験則はありますか?

アップデート

後でこのスレッドにたどり着いた人のために、ここにある最良の説明を見つけました:「Haskellへの穏やかな紹介」。

式をしましょう。

Haskell の let 式は、ネストされたバインディングのセットが必要な場合に便利です。簡単な例として、次のことを考慮してください。

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

let 式によって作成されたバインディングのセットは相互に再帰的であり、パターン バインディングは遅延パターンとして扱われます (つまり、暗黙の ~ を持ちます)。許可される宣言の種類は、型シグネチャ、関数バインディング、およびパターン バインディングのみです。

Where 条項。

where 句を必要とするいくつかの保護された方程式にバインディングのスコープを設定すると便利な場合があります。

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

これは、let 式では実行できないことに注意してください。let 式は、囲まれている式のみをスコープします。where 句は、一連の方程式またはケース式の最上位でのみ使用できます。let 式のバインディングに対する同じプロパティと制約が、where 句のバインディングにも適用されます。ネストされたスコープのこれら 2 つの形式は非常に似ているように見えますが、let 式は式であるのに対し、where 句はそうではないことに注意してください。これは、関数宣言とケース式の構文の一部です。

4

5 に答える 5

45

1: 例の問題

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

パラメータxです。句内のものはwhere、関数のパラメーターf(何もない) と外側のスコープのもののみを参照できます。

2: 最初の例で a を使用するには、次のように、パラメーターとしてwherethe を受け取る 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: いつ使用するletwhereは好みの問題です。let計算を強調するために (計算を前に移動して) 、whereプログラムの流れを強調するために (計算を後ろに移動して) 使用します。

于 2010-12-06T03:00:11.070 に答える
30

ephemient が指摘したガードに関しては技術的な違いがありますが、メインの式を前もって追加の変数を定義して ( where) 事前に定義するか、すべてを前もって定義して式を配置するかという概念的な違いもあります。以下(let)。各スタイルには異なる強調点があり、どちらも数学の論文や教科書などで使用されています。一般に、変数がなければ数式が意味をなさないほど直観的でない変数は、上記で定義する必要があります。コンテキストや名前から直感的にわかる変数は、以下で定義する必要があります。たとえば、ephemient の hasVowel の例では、 の意味vowelsは明らかであるため、その使用法を超えて定義する必要はありません (letガードのために機能しないという事実は無視してください)。

于 2010-12-06T01:36:28.847 に答える
14

法的:

main = print (1 + (let i = 10 in 2 * i + 1))

違法:

main = print (1 + (2 * i + 1 where i = 10))

法的:

hasVowel [] = False
hasVowel (x:xs)
  | x `elem` vowels = True
  | otherwise = False
  where vowels = "AEIOUaeiou"

合法ではない:(MLとは異なり)

let vowels = "AEIOUaeiou"
in hasVowel = ...
于 2010-12-06T01:20:44.207 に答える
10

LYHFGG のこの例が役に立ちました。

ghci> 4 * (let a = 9 in a + 1) + 2  
42  

letlet は式であるため、式を配置できる場所 (!)のどこにでも置くことができます。

つまり、上記の例では、単純に置き換えるために使用することはできません(おそらく、より詳細な式を と組み合わせて使用​​する必要はありません)。whereletcasewhere

于 2015-02-15T07:15:50.980 に答える