简体   繁体   English

使用Maybe monad终止相互递归的函数

[英]Termination of mutually recursive functions using the Maybe monad

Below is are two examples of mutually recursive function pairs. 以下是相互递归函数对的两个示例。 The first example terminates and produces the expected result. 第一个示例终止并生成预期结果。 The second example is similar, except it uses the Maybe monad. 第二个例子类似,只是它使用了Maybe monad。 fun1' does not terminate when called. fun1'在调用时不会终止。

fun1 = 1 + fun2
fun2 = let x = fun1 
       in  2
-- terminates. result is 3.

fun1' = do a <- Just 1 
           b <- fun2'
           return $ a + b
fun2' = do x <- fun1'
           return 2
-- does not terminate.

Here are another two examples. 这是另外两个例子。 Once again, the first example terminates with the expected result and the second example (using the Maybe monad) does not terminate. 再次,第一个示例以预期结果终止,第二个示例(使用Maybe monad)不终止。

fun1'' = fun2'' : [1]
fun2'' = (head . tail $ fun1'') + 1 
-- terminates. result is [2,1]

fun1''' = do a <- Just [1]
             b <- fun2'''
             return $ b : a              
fun2''' = do x <- fun1'''
             return $ (head . tail $ x) + 1
-- does not terminate.

I believe I have a situation that is semantically similar to the last example in my real code. 我相信我的情况在语义上类似于我真实代码中的最后一个例子。 What are my options for getting it to terminate? 我有什么选择让它终止? Will I be forced to abandon the Maybe monad? 我会被迫放弃Maybe monad吗?

Update This is the solution I ended up using; 更新这是我最终使用的解决方案;

fun1'''' = do a <- Just [1]
              b <- fun2''''
              return $ b : a
fun2'''' = do return $ (head . tail . fromJust $ fun1'''') + 1
-- terminates :)

The key difference is that fun2'''' no longer operates on fun1'''' using the bind operator. 关键的区别在于fun2''''不再使用bind运算符对fun1''''进行操作。 Instead it explicitly uses fromJust (and assumes that fun1'''' is not Nothing ). 相反,它明确使用fromJust(并假设fun1''''不是Nothing )。

In my real code fun2 actually calls a number of other functions. 在我的真实代码中, fun2实际上调用了许多其他函数。 These other functions are not mutually recursive with fun2 , and can potentially return a Nothing result. 这些其他函数与fun2不是相互递归的,并且可能会返回Nothing结果。 Fortunately I can still use the bind operator implicitly within the do notation for accessing the other required values. 幸运的是,我仍然可以在do表示中隐式使用bind运算符来访问其他所需的值。

The reason that fun1'/fun2' do not terminate is that the Maybe monad's bind operation ( >>= ) needs to check if the first argument is Nothing or if it is Just (...) . fun1'/fun2'没有终止的原因是Maybe monad的绑定操作( >>= )需要检查第一个参数是否为Nothing或者是否为Just (...) So when you do x <- fun1' in fun2' , even though you do not use x , you still need to check if fun1' is Nothing or Just (...) (you don't care about the (...), it would be bound to x which you don't use anyway). 所以当你在fun2'x <- fun1'时,即使你不使用x ,你还需要检查fun1'Nothing还是Just (...) (你不关心(...) ),它将绑定到你不使用的x )。 But to check that, you need to know if fun2' is Just or Nothing , because you're binding b to the result of it in fun1' ( b <- fun2' ) -> infinite recursion. 但要检查一下,你需要知道fun2'Just还是Nothing ,因为你在fun1'b <- fun2' ) - >无限递归中将b绑定到它的结果。

The same doesn't happen in the first case, because in fun2 , you don't use x , so fun1 never needs to be evaluated! 在第一种情况下不会发生同样的情况,因为在fun2 ,你不使用x ,所以fun1永远不需要被评估!

The problem here is that your terminating function fun1 is not mutually exclusive even though that appears to be. 这里的问题是你的终止函数fun1并不是相互排斥的,即使它似乎是。 Infact, your fun2' function actually just refers to the value 2 . 事实上,你的fun2'函数实际上只是指值2 Example: 例:

λ> let x = undefined in 2
2

The undefined part doesn't get evaluated at all. undefined部分根本没有得到评估。 Hence your x = fun1 will not at all be evaluated in your fun2 function and therefore it terminates successfully since fun2 evaluates to 2 . 因此,你的x = fun1根本不会在你的fun2函数中被评估,因此它会成功终止,因为fun2计算结果为2

Whereas in your fun2' example it doesn't get reduced to any value. 而在你的fun2'例子中,它并没有减少到任何价值。 So, it doesn't terminate. 所以,它不会终止。 Infact, if you actually turn your fun2' function into let expression just like your fun2 example then it will terminate: 事实上,如果您实际上将fun2'函数转换为let表达式,就像您的fun2示例那么它将终止:

fun2' = let x = fun1'
        in (Just 2)

fun2'' is a mutually recursive function which refers to value 2 : fun2''是一个相互递归的函数,它引用值2

λ> tail fun1''
[1]
λ> head $ tail fun1''
1
λ> (head $ tail fun1'') + 1
2

So fun2'' actually refers to a value 2 . 所以fun2''实际上是指一个值2

I think you have no choice but to abandon the Maybe monad. 我想你别无选择,只能放弃Maybe monad。

Remember that monads just represent sequences of computations that can be chained together. 请记住,monads只表示可以链接在一起的计算序列。 The Maybe monad represents computations that can fail and when one of the computations in the sequence fails, we get a Nothing in the end. Maybe monad表示可能失败的计算,当序列中的某个计算失败时,我们最终得到Nothing Though it looks like the value of x is not required (and hence we expect a program halt due to lazy valuation) for the end result, it is in a sequence, and hence has to be evaluated resulting in an infinite recursion. 虽然看起来x的值不是必需的(因此我们期望程序因懒惰的估值而停止),但它是一个序列,因此必须进行评估,从而产生无限递归。

Instead try this: 而是试试这个:

fun1' = do a <- Just 1 
           b <- fun2'
           return $ a + b
fun2' = do Nothing
           fun1'
           return 2
main = print $ fun1'

This will stop because, the Nothing will bypass all other computations. 这将停止,因为, Nothing将绕过所有其他计算。

If you have a need to combine the Maybe monad with mutual recursion, then perhaps you want RecursiveDo notation , which allows monadically calculated values inside an mdo block to refer recursively to each other (for monads supporting the MonadFix class). 如果你需要将Maybe monad与相互递归结合起来,那么也许你想要RecursiveDo表示法 ,它允许mdo块中的monadically计算值以递归方式相互引用(对于支持MonadFix类的monad)。 The following compiles and gives the result Just ([2,1],2) that you probably would expect. 以下编译并给出您可能期望的结果Just ([2,1],2)

{-# LANGUAGE RecursiveDo #-}

maybeFuns = mdo
    fun1''' <- do a <- Just [1]
                  return $ fun2''' : a              
    fun2''' <- return $ (head . tail $ fun1''') + 1
    return (fun1''', fun2''')

You need to define it in one mdo block rather as separate functions, though. 但是,您需要在一个mdo块中定义它,而不是单独的函数。 Although you could replace maybeFuns = by Just (fun1''', fun2''') = if you don't care about getting an error if some part evaluates to Nothing . 虽然你可以替换maybeFuns = by Just (fun1''', fun2''') =如果你不关心如果某个部分评估为Nothing出错。

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

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