不同的 let in for lines in do 块
Different let in for lines in do block
我想使用 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
开头的行创建不同的 inVector
、inInteger
和 outVector
集?
假设您遇到错误:
parse error on input `in'
根据@n.`pronouns'm. 的评论,问题只是缩进。
在大多数情况下,let
块可以用 let
和 in
关键字排列来编写:
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
K。 A. Buhr 的回答是完全正确的。我添加这个答案作为对 Haskell 布局规则方面发生的事情的补充描述,这可能有助于在不同上下文中发生类似问题时进行推理。
几乎所有 Haskell 的语法对缩进完全不敏感,但有一些结构使用对齐来指示语句块或定义中的条目。 do
块就是这样一种情况。不过,所有规则都是相同的。
- 每个块都有其条目的对齐位置。该位置由引入该块的关键字后的第一个非空白字符设置(无论它属于同一行还是后续行)。
- 以完全相同的对齐方式缩进的行是块中“条目”的开头。
- 一行缩进多于对齐没有特别的意义;它只是从较早一行开始的条目的一部分。
- 缩进小于对齐位置的行表示块的结尾(并且 不是 本身是块的一部分,因此它必须是某些封闭语法的一部分,这可能是也可能不是对齐的块)
这样做的一个可能不明显的结果是,当块中的条目本身跨越多行时,每一行的后续行都必须比第一行缩进更多。如果续行的缩进与第一行相同,则它将被视为块中新条目的开始,而不是继续现有的条目,如果缩进较少,则将被视为表示整个块的结尾。
这如何适用于 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 个条目,以 let
、in
、let
和 in
.
开头
这不是 OP 的本意;他们希望有 2 个条目,每个条目都是一个完整的 let ... in ...
表达式。为此,in
关键字需要进一步缩进。
没关系 let ... in ...
本身是一个使用对齐缩进的结构。上述规则中没有任何内容要求 in
与 let
对齐。对齐位置是其中第一个声明的第一个字符(每种情况下 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
块内工作而无需调整,这是我以前从未注意到的好处。
我想使用 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
开头的行创建不同的 inVector
、inInteger
和 outVector
集?
假设您遇到错误:
parse error on input `in'
根据@n.`pronouns'm. 的评论,问题只是缩进。
在大多数情况下,let
块可以用 let
和 in
关键字排列来编写:
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
K。 A. Buhr 的回答是完全正确的。我添加这个答案作为对 Haskell 布局规则方面发生的事情的补充描述,这可能有助于在不同上下文中发生类似问题时进行推理。
几乎所有 Haskell 的语法对缩进完全不敏感,但有一些结构使用对齐来指示语句块或定义中的条目。 do
块就是这样一种情况。不过,所有规则都是相同的。
- 每个块都有其条目的对齐位置。该位置由引入该块的关键字后的第一个非空白字符设置(无论它属于同一行还是后续行)。
- 以完全相同的对齐方式缩进的行是块中“条目”的开头。
- 一行缩进多于对齐没有特别的意义;它只是从较早一行开始的条目的一部分。
- 缩进小于对齐位置的行表示块的结尾(并且 不是 本身是块的一部分,因此它必须是某些封闭语法的一部分,这可能是也可能不是对齐的块)
这样做的一个可能不明显的结果是,当块中的条目本身跨越多行时,每一行的后续行都必须比第一行缩进更多。如果续行的缩进与第一行相同,则它将被视为块中新条目的开始,而不是继续现有的条目,如果缩进较少,则将被视为表示整个块的结尾。
这如何适用于 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 个条目,以 let
、in
、let
和 in
.
这不是 OP 的本意;他们希望有 2 个条目,每个条目都是一个完整的 let ... in ...
表达式。为此,in
关键字需要进一步缩进。
没关系 let ... in ...
本身是一个使用对齐缩进的结构。上述规则中没有任何内容要求 in
与 let
对齐。对齐位置是其中第一个声明的第一个字符(每种情况下 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
块内工作而无需调整,这是我以前从未注意到的好处。