我想在尾递归函数中转换下面的genEdges
函数。
genEdges :: Int -> Node -> IO [Edge]
genEdges n origin | n == 0 = return []
| otherwise = do
edge <- genRandEdge origin
edges <- genEdges (n-1) (snd edge)
return $ edge : edges
我的意思是,最后一行应该是
return $ edge : genEdges (n-1) (snd edge)
虽然我知道edge
和genEdges (n-1) (snd edge)
的类型不同,因此这个示例行不正确。
这可能吗?如果是这样,该功能应该如何?如果没有,为什么?
答案 0 :(得分:2)
这是不可能的。你的尾巴真的是
(genRange origin) >>= (\edge -> .....)
因此它不是递归的。
-----------编辑更新的问题-----------------
一般情况下,如果您有类似
的内容do
a <- foo
bar
你无法使尾部递归出来。这是因为这变得卑鄙于
foo >>= (\a -> bar)
so(&gt;&gt; =)(或(&gt;&gt;))是尾调用。
答案 1 :(得分:1)
修改强>
看看Ingo的回答他是对的,你可能必须将生成器传递给函数然后你可以使用纯函数来获取下一个随机数并将它生成的gen传递给下一个递归函数调用。
结束编辑
你可以创建一个具有累加器的辅助函数,就像这样。我没有编译器,所以我无法保证它完全正确。
genEdges :: Int -> Node -> IO [Edge]
genEdges n o = genEdgesTR n o []
where genEdgesRec n origin acc
| n == 0 = return acc
| otherwise = do
edge <- get RandEdge orgin
genEdgesRec (n-1) (snd edge) (edge : acc)
这可能是用foldM
或其他东西写的,但这取决于你要弄明白。
答案 2 :(得分:1)
如果你最终使用IO
或其他monad / monad变换器,例如RandT
(其他选项正在传递生成器和/或预生成的无限列表),这里有一个使用状态monad的示例变换器,以帮助origin
的线程:
import Control.Monad.State
data Node = Node
type Edge = ((), Node)
genRandEdge :: Node -> IO Edge
genRandEdge = undefined
genEdges :: Int -> Node -> IO [Edge]
genEdges n = evalStateT $ replicateM n $ do
origin <- get
edge <- liftIO $ genRandEdge origin
put $ snd edge
return edge
为了帮助您更好地genRandEdge
以避免IO,我们需要更多上下文,因此请随意提出另一个问题,了解如何改进您的生成方法。