私はHaskellが初めてで、Wherevs.Let。どちらも同様の目的を提供しているようです。私はWherevs.Letの比較をいくつか読みましたが、私はそれぞれをいつ使用するかを識別するのに問題がある。誰かがいくつかのコンテキストを提供してもらえますか?
どこ対レット
where
句は、関数定義のレベルでのみ定義できます。通常、それはlet
定義のスコープと同一です。 唯一の違いは、ガードが使用されている場合です。where
句のスコープは、すべてのガードに拡張されます。対照的に、let
式のスコープは、現在の関数句とガード(存在する場合)のみです。
Haskell Wiki は非常に詳細であり、さまざまなケースを提供しますが、仮想的な例を使用しています。初心者には説明が短すぎると思います。
Letの利点:
f :: State s a
f = State $ \x -> y
where y = ... x ...
whereは、f =に一致するパターンを指し、xはスコープ内にないため、機能しません。対照的に、letで始めた場合は、問題はありません。
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 ...
後でこのスレッドが来る人のために、私はここで見つけられる最も良い説明を見つけました: " 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式の構文の一部です。
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
を使用してプログラムの流れを強調します(計算を後方に移動します)。
Ephemientが指摘したガードに関して技術的な違いがありますが、以下で定義する追加の変数(where
)を使用してメイン式を前に置くか、定義するかには概念的な違いもあります。すべてを事前に行い、以下の式を入れます(let
)。各スタイルには異なる重点があり、両方とも数学の論文、教科書などで使用されています。一般的に、式がそれらなしでは意味をなさないほど十分に直感的でない変数は上記で定義する必要があります。コンテキストまたはその名前のために直感的な変数は、以下で定義する必要があります。たとえば、ephemientのhasVowelの例では、vowels
の意味は明らかであるため、その使用法の上に定義する必要はありません(let
がガードのために機能しないという事実を無視して)。
法的:
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 = ...
[〜#〜] lyhfgg [〜#〜] からこの例を見つけました:
ghci> 4 * (let a = 9 in a + 1) + 2
42
let
は式なので、let
anywhere(!)を式のどこにでも置くことができます。
つまり、上記の例では、where
を使用してlet
を単純に置き換えることはnot可能ですcase
と組み合わせたより詳細なwhere
式を使用します。