【问题标题】:Different let in for lines in do blockdo块中的不同行
【发布时间】:2021-05-10 20:14:26
【问题描述】:

我想使用 hspec 创建一些具有不同值的测试。我写了下面的代码,它没有编译,但给出了我的目标:

spec :: Spec
spec = do
    describe "productOneLine" $ do
        let
            inVector = Data.Vector.replicate 0 0
            inInteger = 3
            outVector = Data.Vector.replicate 1 0
        in
            it "must manage empty vector" $ productOneLine inVector inInteger `shouldBe` outVector
        let
            inVector = Data.Vector.fromList [2, 4, 5]
            inInteger = 4
            outVector = Data.Vector.fromList [9, 6, 1, 2]
        in
            it "must multiply a vector by an integer" $ productOneLine inVector inInteger `shouldBe` outVector

如何为每个以it 开头的 ligne 创建不同的 inVectorinIntegeroutVector 组?

【问题讨论】:

  • 谢谢。但是我想知道如何为 do 块中的各个行插入 let inwhere
  • 请参阅stackoverflow.com/tags/do-notation/info 以及其中链接的答案。 let ... in ... 只是一个表达式。表达式属于do 块的“动作”部分。
  • do 块中没有 where
  • 缩进错误,in 应该比let 缩进更多。原因是do 块有一个let 变体没有in。因此编译器将您的lets 解释为这样的变体,因为它没有立即看到in,然后下一行的in 意外出现in)。否则不清楚代码有什么问题。

标签: haskell let do-notation


【解决方案1】:

假设您遇到了错误:

parse error on input `in'

根据@n.`pronouns'm. 的评论,问题只是缩进。

在大多数情况下,let 块可以写入 letin 关键字排列:

foo a b = let a2 = a*a
              b2 = b*b
          in a2 + b2
          ^
          `- let and in starting at same column

事实上,in 可以比let 缩进更少,它只需要比foo 缩进更多:

foo a b = let a2 = a*a
              b2 = b*b
  in a2 + b2

但是,在 do 块中,允许使用 in-less let 语句:

main = do
  let s = "Hello"
  putStrLn s

如果你尝试写:

main = do
  let s = "Hello"
  in PutStrLn s

let s = "Hello" 被解析为let 语句,in PutStrLn s 被解析为第二条do 语句,语法无效,因为它以保留字in 开头。

你可以写:

main = do
  let s = "Hello" in
    putStrLn s

或:

main = do
  let s = "Hello"
    in putStrLn s

或:

main = do
  let s = "Hello"
    in 
    putStrLn s

在每种情况下,缩进都会导致整个表达式 let ... in ... 被解析为单个表达式,它本身就是一个有效的 do 语句。将两个这样的语句结合起来很容易:

main = do
  let s = "Hello"
    in
    putStrLn s
  let s = "Goodbye"
    in
    putStrLn s

【讨论】:

    【解决方案2】:

    K。 A. 布尔的回答是完全正确的。我将此答案添加为对 Haskell 布局规则方面正在发生的事情的补充描述,这可能有助于在不同上下文中发生类似问题时进行推理。

    几乎所有 Haskell 的语法都对缩进完全不敏感,但是有一些结构使用对齐来指示语句块或定义块中的条目。 do 块就是这样一种上下文。不过,所有这些规则都是相同的。

    1. 每个块都有其条目的对齐位置。位置由引入块的关键字后面的第一个非空白字符设置(不管它是属于同一行还是下一行)。
    2. 以完全相同的对齐方式缩进的行是块中“条目”的开头。
    3. 缩进多于对齐的行没有特殊含义;它只是从前一行开始的条目的一部分。
    4. 缩进小于对齐位置的行表示块的结尾(并且不是本身是块的一部分,因此它必须是某些封闭语法的一部分,这可能是也可能不是对齐的块)

    这样做的结果可能并不明显,当一个块中的条目本身跨越多行时,每一行都必须比第一行进一步缩进。如果续行的缩进与第一行相同,它将被视为在块中开始一个新条目而不是继续现有的条目,如果它的缩进较少,它将被视为指示整个块的结尾。

    这如何应用于 OP 的示例非常简单。

    spec :: Spec
    spec = do
        describe "productOneLine" $ do
            let
                inVector = Data.Vector.replicate 0 0
                inInteger = 3
                outVector = Data.Vector.replicate 1 0
            in
                it "must manage empty vector" $ productOneLine inVector inInteger `shouldBe` outVector
            let
                inVector = Data.Vector.fromList [2, 4, 5]
                inInteger = 4
                outVector = Data.Vector.fromList [9, 6, 1, 2]
            in
                it "must multiply a vector by an integer" $ productOneLine inVector inInteger `shouldBe` outVector
    

    do 关键字后面的第一个字符是let 中的 l(在下一行)。所以我们可以立即看到这个do 块有4 个条目,以letinletin 开头。

    这不是 OP 的意图;他们希望有 2 个条目,每个条目都是完整的 let ... in ... 表达式。为此,in 关键字需要进一步缩进。

    let ... in ... 本身是一个使用对齐缩进的构造没关系。上述规则中没有任何内容要求inlet 对齐。对齐位置是它们内部第一个声明的第一个字符(inVector 中的 i),而不是 l 的位置,它仅适用于声明块,不适用于 in 部分整个let ... in ... 表达式。

    事实上let ... in ...in 关键字对缩进完全不敏感。它可以去任何地方(只要它不违反封闭块设置的对齐方式,就像在 OP 的示例中发生的那样)。你可以这样做1

    three = let x = 1
                y = 2
      in x + y
    

    或者这个2

    five = let x = 2
               y = 3 in x + y
    

    无论哪种方式布局规则都不需要告诉in关键字不是声明块的一部分,只需要告诉块中每个声明的开始位置。


    1 在此示例中,in 的放置只有一个约束。它不能在行首直接走,这只是因为模块中的全局定义(跨越整个文件)实际上是一个对齐的块!您通常不会注意到这一点,因为通常对此块使用零缩进对齐。

    2我平时的风格是这样的:

    seven
      = let x = 3
            y = 4
         in x + y
    

    它恰好在 do 块内工作而无需调整,这是我以前从未注意到的奖励。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-10-21
      • 2016-04-10
      • 2016-10-23
      • 2019-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多