monad中的递归

时间:2014-11-21 12:59:22

标签: haskell

我无法理解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块的最终值?为什么在递归中重复调用最终返回行?我知道返回不是命令式语言中的函数返回,但我看不出这条线是如何重复的。

我知道这是一个原始的初学者问题,但我很难过。

2 个答案:

答案 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; (绑定)连续值。