首页 文章

Haskell:Where vs. Let

提问于
浏览
94

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

Where与Let一个where子句只能在函数定义的级别定义 . 通常,这与let定义的范围相同 . 唯一的区别是使用防护装置 . where子句的范围扩展到所有警卫 . 相反,let表达式的范围只是当前的函数子句和guard,如果有的话 .

Haskell Cheat Sheet

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

Advantages of Let

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

Control.Monad.State

不起作用,因为where指的是模式匹配f =,其中没有x在范围内 . 相反,如果你开始使用let,那么你就不会有麻烦了 .

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

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 ...
  • 在第一个例子中,为什么 Let 在范围内但 Where 不是?

  • 是否可以将 Where 应用于第一个示例?

  • 有些人可以将此应用于变量代表实际表达式的实例吗?

  • 是否有一般的经验法则可以遵循何时使用?


更新

对于那些稍后通过这个帖子来的人,我找到了最好的解释:“A Gentle Introduction to Haskell” .

让表达 . 只要需要嵌套的绑定集,Haskell的let表达式就很有用 . 举个简单的例子,考虑:让y = a * b
f x =(x y)/ y
在f c f d
由let表达式创建的绑定集是相互递归的,并且模式绑定被视为惰性模式(即它们带有隐式〜) . 允许的唯一类型的声明是类型签名,函数绑定和模式绑定 . 条款 . 有时,将绑定范围扩展到几个保护方程是很方便的,这需要一个where子句:f x y | y> z = ...
| y == z = ...
| y <z = ...
其中z = x * x
请注意,这不能通过let表达式来完成,该表达式仅覆盖它所包含的表达式 . where子句仅允许在一组方程或case表达式的顶层 . let表达式中绑定的相同属性和约束适用于where子句中的绑定 . 这两种形式的嵌套作用域看起来非常相似,但请记住let表达式是一个表达式,而where子句则不是 - 它是函数声明和case表达式语法的一部分 .

4 回答

  • 3

    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:何时使用 letwhere 是品味问题 . 我使用 let 强调计算(通过将其移动到前面)和 where 来强调程序流(通过将计算移动到后面) .

  • 11

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

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

    let 是一个表达式,所以你可以放一个 let anywhere(!) 表达式 .

    换句话说,在上面的例子中, not 可以使用 where 来简单地替换 let (没有使用更复杂的 case 表达式和 where ) .

  • 33

    法律:

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

    虽然ephemient所指出的守卫方面存在技术差异,但是你是否想要将主要公式预先设置为下面定义的额外变量( where ),或者是否要预先定义所有内容并放置公式如下( let ) . 每种风格都有不同的重点,你会看到它们都用在数学论文,教科书等中 . 一般来说,变量足够不直观,公式不具有例子, vowels 的含义是显而易见的,因此不需要在其上面定义用法(无视 let 由于警卫而无法工作的事实) .

相关问题