我无法理解monad中的递归。从haskell.org wiki这里有一个例子:
main = f 3
f 0 = return []
f n = do v <- getLine
vs <- f (n-1)
return $! v : vs
该程序递归地从标准输入获得三行。我无法理解的是当你到达f 0时以及递归如何解开时会发生什么。如何构造do块的最终值?为什么在递归中重复调用最终返回行?我知道返回不是命令式语言中的函数返回,但我看不出这条线是如何重复的。
我知道这是一个原始的初学者问题,但我很难过。
答案 0 :(得分:9)
在这种特殊情况下,您可以完全展开。也许这会有所帮助:
f 3
= { reduce f (and the subtraction) }
do v <- getLine
vs <- f 2
return $! v : vs
= { reduce f (and the subtraction) }
do v <- getLine
vs <- do v' <- getLine
vs' <- f 1
return $! v' : vs'
return $! v : vs
= { reduce f (and the subtraction) }
do v <- getLine
vs <- do v' <- getLine
vs' <- do v'' <- getLine
vs'' <- f 0
return $! v'' : vs''
return $! v' : vs'
return $! v : vs
= { reduce f }
do v <- getLine
vs <- do v' <- getLine
vs' <- do v'' <- getLine
vs'' <- return []
return $! v'' : vs''
return $! v' : vs'
return $! v : vs
=
...
此时,没有任何递归。我们所做的只是应用函数定义。 如果我们假设monad法则成立,我们可以从这里进一步简化:
...
= { vs'' <- return [] means that vs'' is [] }
do v <- getLine
vs <- do v' <- getLine
vs' <- do v'' <- getLine
return $! v'' : []
return $! v' : vs'
return $! v : vs
= { inline the innermost do block }
do v <- getLine
vs <- do v' <- getLine
v'' <- getLine
vs' <- return $! v'' : []
return $! v' : vs'
return $! v : vs
= { inline return $! v'' : [] }
do v <- getLine
vs <- do v' <- getLine
v'' <- getLine
return $! v' : v'' : []
return $! v : vs
= { inline the innermost do block }
do v <- getLine
v' <- getLine
v'' <- getLine
vs <- return $! v' : v'' : []
return $! v : vs
= { inline return $! v' : v'' : [] }
do v <- getLine
v' <- getLine
v'' <- getLine
return $! v : v' : v'' : []
答案 1 :(得分:3)
你可以&#34;伪编译&#34; /&#34;展开&#34; monadic块看看它是如何工作的:
f 3 = do v <- getLine
vs <- f 2 -- (will return a list with 2 lines from stdin
return $! v : vs
f 2 = do v <- getLine
vs <- f 1 -- (will return singleton list with a line from stdin)
return $! v : vs
f 1 = do v <- getLine
vs <- f 0 -- (will return an empty list)
return $! v : vs
f 0 = return []
因此,它会getLine
3次,然后建立一个行列表,第一行将是列表中的第一个值。
每次在monadic表达式中看到return
时,都会在其中放置一个值。每次在monadic表达式中看到<-
(绑定)时,您都会从中获取值。在IO
monad的情况下,你总是拿出并取出一个monad。相反,列表monad可以&#34;取出&#34; (绑定)连续值。