简体   繁体   English

单子函数中的尾递归

[英]Tail recursion in a monadic function

I wanted to transform the folowing genEdges function in a tail recursive one. 我想将以下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

I mean, the last line should be something like 我的意思是,最后一行应该是这样的

return $ edge : genEdges (n-1) (snd edge)

although I know that the types of edge and genEdges (n-1) (snd edge) are different and hence this example line is incorrect. 尽管我知道edgegenEdges (n-1) (snd edge)类型不同,因此此示例行不正确。

Is that possible? 那可能吗? If so, how should be the function? 如果是这样,功能应该如何? If not, why? 如果没有,为什么?

It's impossible. 不可能。 Your tail call is really 你的尾巴真的是

(genRange origin) >>= (\edge -> .....)

and hence it is not recursive. 因此它不是递归的。

----------- Edit for updated question ----------------- -----------编辑更新的问题-----------------

Generally, if you have something like 通常,如果您有类似

do
  a <- foo
  bar

you can't make something tail recursive out of it. 您无法从中进行尾递归处理。 This is because this gets desugared to 这是因为这被贬低了

foo >>= (\a -> bar)

so (>>=) (or (>>)) is the tail call. 因此(>> =)(或(>>))是尾部调用。

EDIT 编辑

Looking at Ingo's answer he is right, you'd probably have to pass the generator into the function then you can use a pure function to get the next random number and pass the gen that it produces to the next recursive function call. 看Ingo的回答,他是对的,您可能必须将生成器传递到函数中,然后可以使用纯函数获取下一个随机数,并将生成的gen传递给下一个递归函数调用。

END EDIT 结束编辑

You can make a helper function that has an accumulator so like this. 您可以创建一个具有累加器的辅助函数,如下所示。 I don't have a compiler so I can't guaranteed it is exactly correct. 我没有编译器,所以不能保证它完全正确。

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)

This could probably be written with foldM or something but that is up to you to figure out. 这可能是用foldM或其他东西编写的, foldM取决于您自己确定。

If you end up using IO or other monad/monad transformer such as RandT (other options are passing generators and/or pregenerated infinite lists around), here is an example of using the state monad transformer to help with threading of origin : 如果最终使用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

To help you with better genRandEdge to avoid IO we need more context so feel free to ask another question on how your generation approach can be improved. 为了帮助您更好地使用genRandEdge来避免IO,我们需要更多的上下文,因此随时可以提出另一个问题来改进您的生成方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM