在this问题中,Will的答案会说明以下代码(我们称之为code A
):
reverse2lines :: IO ()
reverse2lines =
do line1 <- getLine
line2 <- getLine
putStrLn (reverse line2)
putStrLn (reverse line1)
可以转换为以下内容(让我们称之为code B
):
reverse2lines =
do { line1 <- getLine ;
do { line2 <- getLine ;
do { putStrLn (reverse line2) ;
do { putStrLn (reverse line1) } } } }
我很困惑。 我理解,例如,
addOneInt :: IO ()
addOneInt = do line <- getLine
putStrLn (show (1 + read line :: Int))
可以转化为:
addOneInt' :: IO ()
addOneInt' = getLine >>= \line ->
putStrLn (show ( 1 + read line :: Int))
但我不明白嵌套do
转换的工作原理。换句话说,我不明白从code A
到code B
的人是怎么来的?管理这种转变的规则是什么?
这些规则在何处描述/解释/指定?
有人可以通过这个(codeA
到codeB
)转换解释一下这里发生了什么吗?
例如,do { command1; do {command2 } }
是什么意思?我该怎么解释呢?它的定义是什么?
换句话说,
之间有什么区别
do {command1;command2}
和
do {command1; do {command2}}
?
此外,
之间有什么区别 do {command1; do{command2};command3}
和
do {command1;do {command2; do {command3}}}
?
感谢阅读。
答案 0 :(得分:5)
完全单独处理do
表达式:嵌套它们并不会改变它们的去除方式。举个例子,我们可以从底线开始:
reverse2lines =
do { line1 <- getLine ;
do { line2 <- getLine ;
do { putStrLn (reverse line2) ;
putStrLn (reverse line1) } } }
然后是下一个:
reverse2lines =
do { line1 <- getLine ;
do { line2 <- getLine ;
putStrLn (reverse line2) >> putStrLn (reverse line1) } }
实际上就像:
reverse2lines =
do { line1 <- getLine ;
do { line2 <- getLine ;
putStrLn (reverse line2)
putStrLn (reverse line1) } }
然后我们可以将内部剩余的do
变成一个lambda:
reverse2lines =
do { line1 <- getLine ;
getLine >>= \ line2
putStrLn (reverse line2) >>
putStrLn (reverse line1) }
然后,如果我们倒退,我们会发现它与以下相同:
reverse2lines =
do { line1 <- getLine ;
line2 <- getLine ;
putStrLn (reverse line2) ;
putStrLn (reverse line1) }
因此,正如您通过整个示例所看到的,嵌套版本与平面版本相同。事实上,如果你只看一下des do
表达式的规则(我们已经看到了重要的那些),你会发现像这样嵌套它们不会改变任何东西。
事实上,这很自然,因为do
的方式是递归定义的:每当我们去掉一行时,我们就会在它后面的所有行上递归,好像它们是另一行do
表达