Haskell将“case of”语句转换为尾递归代码

时间:2012-11-21 22:26:10

标签: haskell recursion tail

我需要帮助。 我有这种形式的功能

myFunction = case myFunction of
    (Nothing) -> (Just)
    (Just) -> (Just)

我想让它尾递归。 如何做到这一点? 我理解,根据递归调用的返回,我们有一个不同的语句,这使得它很难(我需要帮助的原因^^)。 我可以提供原始功能,但我更愿意寻找更通用的解决方案。 提前致谢

修改:实际代码:

myFunction :: MyTree x -> (x, Maybe(MyTree x))
myFunction = (x, Nothing)
myFunction (MyNode left right) = case myFunction left of
                                      (x, Nothing)    -> (x, Just right)
                                      (x, Just left2) -> (x, Just (Node left2 right))

2 个答案:

答案 0 :(得分:1)

我假设你定义了

data MyTree x = MyLeaf x | MyNode (MyTree x) (MyTree x) 

并且意味着

myFunction :: MyTree x -> (x, Maybe(MyTree x))
myFunction (MyLeaf x) = (x, Nothing)
myFunction (MyNode left right) = case myFunction left of
                                      (x, Nothing)    -> (x, Just right)
                                      (x, Just left2) -> (x, Just (MyNode left2 right))

这是一个拉出最左边的叶子并将相应的右分支缝合在一起的函数。

你问如何使这个尾递归。这是为什么?在一些(严格)语言中,尾递归代码更有效,但Haskell使用惰性求值,这意味着递归调用发生的时间并不重要,而是它们产生输出的时间。在这种情况下,头部递归case myFunction left of向下缩放到树下直到找到最左边的叶子,你不能更快地到达它。然而,在回来的路上,它确实通过了x而不是立即返回,但是它也在适当的平面上重新缝合所有正确的分支而没有任何记账,这是使用递归的乐趣在递归数据结构上。

请参阅this question,了解为什么尾递归不是Haskell中效率最重要的因素。

在节点上使用数据对二叉树做的三件经典事情是:  1.预先遍历遍历(先访问当前节点然后再访问左子树) - 双尾递归  2.有序遍历(访问左子树,然后是当前节点,然后右) - 头部和尾部递归  3.后序遍历(在当前节点之前访问左右子树) - 双头递归。

对于不习惯懒惰评估的人来说,发布命令听起来令人担忧,但这是一种有效的方法来对树中的值求和,例如,特别是如果你确保编译器知道它是严格的。

与往常一样,最好的算法可以提供最快的结果,如果要打开优化,则应该使用-O2进行编译。

答案 1 :(得分:0)

这些必须匹配。

myFunction = ...
myFunction (MyNode left right) = ...

他们不匹配。你不能一起使用它们。为什么?其中一个采用零参数,另一个采用一个参数。他们必须采用相同数量的论点。如果您需要忽略参数,请使用_。请注意,使用_的版本必须位于不使用_的版本之后。

myFunction :: MyTree x -> Maybe (MyTree x)
myFunction (MyNode left right) =
  case myFunction left of
    Nothing -> Just right
    Just left2 -> Just (MyNode left2 right)
myFunction _ = Nothing

我不知道x应该在你的函数体中是什么。它不受任何限制。

这不是尾递归。并非所有函数都可以构成尾递归函数。

提示

也许如果您描述了该功能应该做什么,我们可以帮助您做到这一点。