137

我是 Haskell 的新手,我对Where vs. Let感到非常困惑。它们似乎都提供了相似的目的。我已经阅读了WhereLet之间的一些比较,但我无法辨别何时使用它们。有人可以提供一些上下文或一些示例来演示何时使用一个而不是另一个?

在哪里与让

where子句只能在函数定义级别定义。通常,这与定义的范围相同let唯一的区别是何时使用警卫。该where条款的范围涵盖所有警卫。相反,let表达式的范围只是当前函数子句和保护,如果有的话。

Haskell 备忘单

Haskell Wiki非常详细,提供了各种案例,但它使用了假设的示例。我发现它的解释对于初学者来说太简短了。

Let的优点

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

控制单子状态

将不起作用,因为 where 指的是匹配 f = 的模式,其中没有 x 在范围内。相反,如果您从 let 开始,那么您将不会遇到麻烦。

Haskell Wiki 关于 Let 的优点

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 表达式来完成,它只作用于它所包含的表达式。where 子句只允许在一组方程式或案例表达式的顶层使用。let 表达式中绑定的相同属性和约束适用于 where 子句中的那些。这两种形式的嵌套范围看起来非常相似,但请记住,let 表达式是表达式,而 where 子句不是——它是函数声明和 case 表达式语法的一部分。

4

5 回答 5

45

1:示例中的问题

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

是参数x。子句中的事物where只能引用函数的参数f(没有)和外部范围内的事物。

2:要where在第一个示例中使用 a ,您可以引入第二个以 为参数的命名函数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:什么时候用let还是看where个人口味。我let用来强调计算(通过将其移到前面)并where强调程序流程(通过将计算移到后面)。

于 2010-12-06T03:00:11.070 回答
30

where虽然ehemient指出的守卫方面存在技术差异,但在概念上也存在差异,即您是想将主要公式放在前面,并在下面定义额外的变量(( let) 以下。每种风格都有不同的侧重点,您会在数学论文、教科书等中看到两者都使用过。通常,应该在上面定义足够不直观以至于公式没有它们就没有意义的变量;由于上下文或其名称而直观的变量应在下面定义。例如,在 ehemient 的 hasVowel 示例中,of 的含义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  

let是一个表达式,因此您可以在let 任何地方(!)放置表达式可以去的地方。

换句话说,在上面的示例中,不可能使用where简单的替换(可能不使用与 结合的let更详细的表达式)。casewhere

于 2015-02-15T07:15:50.980 回答
9

可悲的是,这里的大多数答案对于初学者来说都太技术性了。

LHYFGG有一个相关的章节——如果你还没有阅读过,你应该阅读它,但本质上是:

  • where只是一个语法结构(不是糖),仅在函数定义中有用。
  • 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:

于 2019-09-18T12:14:21.737 回答