web-dev-qa-db-ja.com

Haskell:Where vs. Let

私はHaskellが初めてで、Wherevs.Let。どちらも同様の目的を提供しているようです。私はWherevs.Letの比較をいくつか読みましたが、私はそれぞれをいつ使用するかを識別するのに問題がある。誰かがいくつかのコンテキストを提供してもらえますか?

どこ対レット

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で始めた場合は、問題はありません。

レットの利点に関するHaskell Wiki

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

Whereの利点

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句は、一連の方程式またはcase式の最上位レベルでのみ許可されます。 let式のバインディングの同じプロパティと制約は、where句のプロパティと制約に適用されます。ネストされたスコープのこれら2つの形式は非常に似ているように見えますが、let式は式であり、where句はそうではないことに注意してください。これは関数宣言とcase式の構文の一部です。

105
user295190

1:例の問題

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

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

2:最初の例でwhereを使用するには、次のように、xをパラメーターとして取る2番目の名前付き関数を導入できます。

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

またはこのように:

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

3:これは...のない完全な例です:

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を使用してプログラムの流れを強調します(計算を後方に移動します)。

35
antonakos

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

24
gdj

法的:

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 = ...
11
ephemient

[〜#〜] lyhfgg [〜#〜] からこの例を見つけました:

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

letは式なので、letanywhere(!)を式のどこにでも置くことができます。

つまり、上記の例では、whereを使用してletを単純に置き換えることはnot可能ですcaseと組み合わせたより詳細なwhere式を使用します。

3
jhegedus