微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

do 块中的不同行

如何解决do 块中的不同行

我想使用 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

如何为每个以 inVector 开头的法线创建不同的 inIntegeroutVectorit 集?

解决方法

假设您收到错误:

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 块中,允许使用无 inlet 语句:

main = do
  let s = "Hello"
  putStrLn s

如果您尝试编写:

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

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

你可以写:

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
,

K。 A. Buhr 的回答是完全正确的。我添加此答案作为对 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 ... 部分。

事实上,inlet ... 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 块内工作而无需调整,这是我以前从未注意到的好处。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。