Haskell: Where vs Let

我是新的哈斯克尔和我非常困惑的 在哪里。它们似乎都提供了一个相似的目的。我已经阅读了一些 在哪里之间的比较,但是我很难分辨什么时候使用它们。能否有人提供一些上下文或者一些例子来说明什么时候应该使用一个而不是另一个?

哪里 VS 让

where子句只能在函数定义级别定义。通常,这与 let定义的范围相同。唯一的区别是当警卫被使用的时候.where子句的范围扩展到所有警卫。相反,let表达式的作用域只是当前函数子句和约束(如果有的话)。

Haskell 小抄

Haskell Wiki非常详细,提供了各种情况,但它使用了假设的例子。我觉得对于一个初学者来说,它的解释太简短了。

Let 的优点:

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

控制中心,单子,国务院

将不会工作,因为在哪里指 模式匹配 f = ,其中没有 x 在范围内。相反,如果你有 从 let 开始,然后你就不会 有麻烦了。

Haskell Wiki 关于 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 提到,在哪里子句是声明性的,而 表达式是表达性的。除了风格之外,他们的表现有什么不同?

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 y   = a*b
f x = (x+y)/y
in f c + f d

由 let 创建的绑定集 表达式是相互递归的,并且 模式绑定被视为懒惰 模式(即它们带有一个隐含的 唯一的宣言 允许的是类型签名, 函数绑定和模式 束缚。

凡条款。

有时候进行范围调查是很方便的 绑在几个守卫上 这需要一个位置 子句:

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

注意,这不能用 let 表达式来完成,因为 let 表达式只覆盖它所包含的表达式。Where 子句只能在一组方程式或大小写表达式的顶层使用。Let 表达式中绑定的相同属性和约束也适用于 where 子句中的绑定。这两种形式的嵌套作用域看起来非常相似,但是请记住,let 表达式是一个表达式,而 where 子句不是——它是函数声明和大小写表达式语法的一部分。

65966 次浏览

法律:

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

虽然在防守方面存在技术上的差异,但是在你是想把主要的公式放在前面,还是想把公式放在后面(where) ,还是想把所有的东西都放在前面(let) ,还是存在概念上的差异。每种风格都有不同的侧重点,你可以在数学论文、教科书等中看到它们的使用。一般来说,那些非常不直观的变量,如果没有它们,公式就没有意义,应该在上面定义; 那些由于上下文或名称而非常直观的变量应该在下面定义。例如,在 ephemient 的 hasVowel 例子中,vowels的含义是显而易见的,因此它不需要在其用法之上进行定义(忽略了 let由于保护而无法工作的事实)。

例子中的问题

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

是参数 xwhere子句中的内容只能引用函数 f的参数(没有参数)和外部作用域中的内容。

2: 要在第一个示例中使用 where,可以引入第二个命名函数 以 x作为参数,如下所示:

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

或者像这样:

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

他说: 这是一个没有 ...的完整例子:

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)

什么时候使用 let或者 where取决于品味。我使用 let强调计算(将其移到前面) ,使用 where强调程序流(将计算移到后面)。

我发现 LYHFGG的这个例子很有帮助:

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

let是一个表达式,所以你可以把 let 任何地方放在表达式可以去的地方。

换句话说,在上面的例子中,没有可以使用 where来简单地替换 let(也许不需要使用更详细的 case表达式与 where结合)。

遗憾的是,这里的大多数答案对于初学者来说太专业了。

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 中定义的名称对输出函数(在 |之前的部分)以及绑定之后的所有谓词和部分都是可见的。所以我们可以让我们的函数只返回人们的 BMI > = 25: