Haskell:Where 与 Let

发布于 2024-10-06 11:53:08 字数 3300 浏览 12 评论 0 原文

我是 Haskell 的新手,我对 WhereLet 感到非常困惑。它们似乎都提供了相似的目的。我读过一些 WhereLet 之间的比较,但我很难辨别何时使用它们。有人可以提供一些背景信息或者一些示例来说明何时使用其中一种而不是另一种吗?

哪里与让

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 关于 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 ...
  1. 在第一个示例中,为什么 Let 在范围内,而 Where 不在范围内?
  2. 是否可以将Where应用于第一个示例?
  3. 有人可以将其应用于变量代表实际表达式的真实示例吗?
  4. 何时使用每种方法是否有可遵循的一般经验法则?

更新

对于那些稍后通过该线程的人,我发现可以在这里找到最好的解释:“Haskell 简介”。

Let 表达式。

Haskell 的 let 表达式很有用 每当一组嵌套的绑定 必需的。举个简单的例子, 考虑:

令 y = a*b
    fx = (x+y)/y
在 fc + fd

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

Where 子句。

有时确定范围很方便 多个受保护的绑定 方程,这需要一个 where 子句:

<前><代码>fxy | y>z = ... | y==z = ... | y

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

I am new to Haskell and I am very confused by Where vs. Let. They both seem to provide a similar purpose. I have read a few comparisons between Where vs. Let but I am having trouble discerning when to use each. Could someone please provide some context or perhaps a few examples that demonstrate when to use one over the other?

Where vs. Let

A where clause can only be defined at the level of a function definition. Usually, that is identical to the scope of let definition. The only difference is when guards are being used. The scope of the where clause extends over all guards. In contrast, the scope of a let expression is only the current function clause and guard, if any.

Haskell Cheat Sheet

The Haskell Wiki is very detailed and provides various cases but it uses hypothetical examples. I find its explanations too brief for a beginner.

Advantages of Let:

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

Control.Monad.State

will not work, because where refers to
the pattern matching f =, where no x
is in scope. In contrast, if you had
started with let, then you wouldn't
have trouble.

Haskell Wiki on Advantages of Let

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

Advantages of 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)

Declaration vs. Expression

The Haskell wiki mentions that the Where clause is declarative while the Let expression is expressive. Aside from style how do they perform differently?

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. In the first example why is the Let in scope but Where is not?
  2. Is it possible to apply Where to the first example?
  3. Can some apply this to real examples where the variables represent actual expressions?
  4. Is there a general rule of thumb to follow when to use each?

Update

For those that come by this thread later on I found the best explanation to be found here: "A Gentle Introduction to Haskell".

Let Expressions.

Haskell's let expressions are useful
whenever a nested set of bindings is
required. As a simple example,
consider:

let y   = a*b
    f x = (x+y)/y
in f c + f d

The set of bindings created by a let
expression is mutually recursive, and
pattern bindings are treated as lazy
patterns (i.e. they carry an implicit
~). The only kind of declarations
permitted are type signatures,
function bindings, and pattern
bindings.

Where Clauses.

Sometimes it is convenient to scope
bindings over several guarded
equations, which requires a where
clause:

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

Note that this cannot be done with a let expression, which only scopes over the expression which it encloses. A where clause is only allowed at the top level of a set of equations or case expression. The same properties and constraints on bindings in let expressions apply to those in where clauses. These two forms of nested scope seem very similar, but remember that a let expression is an expression, whereas a where clause is not -- it is part of the syntax of function declarations and case expressions.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

流殇 2024-10-13 11:53:10

1:示例中的问题

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

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: 何时使用 < code>let 或 where 取决于个人喜好。我使用 let 来强调计算(通过将其移到前面),使用 where 来强调程序流程(通过将计算移到后面)。

1: The problem in the example

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

is the parameter x. Things in the where clause can refer only to the parameters of the function f (there are none) and things in outer scopes.

2: To use a where in the first example, you can introduce a second named function
that takes the x as a parameter, like this:

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

or like this:

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

3: Here is a complete example without the ...'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: When to use let or where is a matter of taste. I use let to emphasize a computation (by moving it to the front) and where to emphasize the program flow (by moving the computation to the back).

薄情伤 2024-10-13 11:53:10

虽然 ephemient 指出的守卫方面存在技术差异,但在概念上也存在差异,即您是否要将主公式与下面定义的额外变量(where)一起放在前面,或者您是否想要预先定义所有内容并将公式放在下面 (let)。每种风格都有不同的侧重点,并且您会在数学论文、教科书等中看到两种风格的使用。一般来说,那些非常不直观的变量,如果没有它们,公式就没有意义,应该在上面定义;由于上下文或其名称而直观的变量应在下面定义。例如,在 ehemient 的 hasVowel 示例中,vowels 的含义很明显,因此不需要在其用法之上定义它(忽略 let 由于以下原因而无法工作的事实)警卫)。

While there is the technical difference with respect to guards that ephemient pointed out, there is also a conceptual difference in whether you want to put the main formula upfront with extra variables defined below (where) or whether you want to define everything upfront and put the formula below (let). Each style has a different emphasis and you see both used in math papers, textbooks, etc. Generally, variables that are sufficiently unintuitive that the formula doesn't make sense without them should be defined above; variables that are intuitive due to context or their names should be defined below. For example, in ephemient's hasVowel example, the meaning of vowels is obvious and so it need not be defined above its usage (disregarding the fact that let wouldn't work due to the guard).

我最亲爱的 2024-10-13 11:53:10

合法:

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

Legal:

main = print (1 + (let i = 10 in 2 * i + 1))

Not legal:

main = print (1 + (2 * i + 1 where i = 10))

Legal:

hasVowel [] = False
hasVowel (x:xs)
  | x `elem` vowels = True
  | otherwise = False
  where vowels = "AEIOUaeiou"

Not legal: (unlike ML)

let vowels = "AEIOUaeiou"
in hasVowel = ...
傲影 2024-10-13 11:53:10

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

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

Sadly, most of the answers here are too technical for a beginner.

LHYFGG has a relevant chapter on it -which you should read if you haven't already, but in essence:

  • where is just a syntactic construct (not a sugar) that are useful only at function definitions.
  • let ... in is an expression itself, thus you can use them wherever you can put an expression. Being an expression itself, it cannot be used for binding things for guards.

Lastly, you can use let in list comprehensions too:

calcBmis :: (RealFloat a) => [(a, a)] -> [a]
calcBmis xs = [bmi | (w, h) <- xs, let bmi = w / h ^ 2, bmi >= 25.0]
-- w: width
-- h: height

We include a let inside a list comprehension much like we would a predicate, only it doesn't filter the list, it only binds to names. The names defined in a let inside a list comprehension are visible to the output function (the part before the |) and all predicates and sections that come after of the binding. So we could make our function return only the BMIs of people >= 25:

要走干脆点 2024-10-13 11:53:10

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

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

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

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

I found this example from LYHFGG helpful:

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

let is an expression so you can put a let anywhere(!) where expressions can go.

In other words, in the example above it is not possible to use where to simply replace let (without perhaps using some more verbose case expression combined with where).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文