我是Haskell的新手,我对哪里与让感到困惑。它们似乎都提供了类似的目的。我已经阅读了哪里与让之间的一些比较,但我无法辨别何时使用每一项。有人可以提供一些上下文或者一些例子来说明何时使用其中一个?
Where vs. Let
where
子句只能在函数定义的级别定义。通常,这与let
定义的范围相同。 唯一的区别是使用防护装置。where
子句的范围扩展到所有警卫。相反,let
表达式的范围只是当前的函数子句和保护(如果有的话)。
Haskell Wiki非常详细并提供了各种案例,但它使用了假设的例子。我觉得它的解释对初学者来说太短暂了。
让:
的优势f :: State s a
f = State $ \x -> y
where y = ... x ...
不起作用,因为在哪里指的是 模式匹配f =,其中没有x 在范围内。相反,如果你有 从let开始,然后你不会 有麻烦。
Haskell Wiki on Advantages of 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 子句是声明性的,而让表达式具有表现力。除了风格,它们的表现如何不同?
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 ...
对于那些后来通过这个帖子的人,我找到了最好的解释:“A Gentle Introduction to Haskell”。
让表达。
Haskell的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表达式无法做到这一点,该表达式仅覆盖它所包含的表达式。 where子句仅允许在一组方程或case表达式的顶层。 let表达式中绑定的相同属性和约束适用于where子句中的绑定。这两种形式的嵌套范围看起来非常相似,但请记住let表达式是一个表达式,而where子句则不是 - 它是函数声明和case表达式语法的一部分。
答案 0 :(得分:34)
1:示例中的问题
f :: State s a
f = State $ \x -> y
where y = ... x ...
是参数x
。 where
子句中的内容只能引用函数f
的参数(没有)和外部作用域中的内容。
2:要在第一个示例中使用where
,您可以引入第二个命名函数
将x
作为参数,如下所示:
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
来强调程序流(通过将计算移动到后面)。
答案 1 :(得分:23)
虽然ephemient指出的守卫存在技术差异,但是你是否想要将主要公式预先设置为下面定义的额外变量(where
)或者你是否想要预先定义所有内容并将公式放在下面(let
)。每种风格都有不同的重点,你会看到它们都用在数学论文,教科书等中。一般来说,如果没有它们,那么公式就没那么有意义的变量应该在上面定义;由于上下文或其名称而直观的变量应在下面定义。例如,在ephemient的hasVowel示例中,vowels
的含义是显而易见的,因此不需要在其使用之上定义(忽略let
因保护而无效的事实。)
答案 2 :(得分:11)
法律:
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 = ...
答案 3 :(得分:3)
我在LYHFGG帮助中找到了这个例子:
ghci> 4 * (let a = 9 in a + 1) + 2
42
let
是一个表达式,因此您可以将let
置于表达式所在的任何位置(!)。
换句话说,在上面的例子中,使用where
来简单地替换let
<(可能没有使用更详细的case
表达式)结合where
)。
答案 4 :(得分:1)
遗憾的是,这里的大多数答案对于初学者来说太技术性了。
LHYFGG上有相关的章节-如果您尚未阅读,则应阅读,但本质上是:
where
只是一种语法构造(不是 sugar ),仅在函数定义中有用。let ... in
是表达式本身,因此您可以在可以放置表达式的任何地方使用它们。作为一个表达式本身,它不能用于约束守卫人员。最后,您也可以在列表推导中使用 let
:
calcBmis :: (RealFloat a) => [(a, a)] -> [a]
calcBmis xs = [bmi | (w, h) <- xs, let bmi = w / h ^ 2, bmi >= 25.0]
-- w: width
-- h: height
我们在列表理解中包含一个 let ,就像我们的谓词一样,只是它不过滤列表,仅绑定到名称。列表理解内的let中定义的名称对输出函数(
|
之前的部分)以及绑定之后的所有谓词和部分可见。因此,我们可以使函数仅返回> = 25的人的BMI: