约翰休斯的“折叠树”怎么样?我误解了吗?

时间:2015-01-13 17:46:57

标签: haskell fold type-mismatch algebraic-data-types

John Hughes在他着名的文章Why Functional Programming Matters中描述了列表和有序标记树的数据类型,

listof * ::= Nil | Cons * (listof *)
treeof * ::= Node * (listof (treeof *))

和一个名为foldtree的函数,

foldtree f g a (Node label subtrees) =
         f label (foldtree f g a subtrees)

foldtree f g a (Cons subtree rest) =
         g (foldtree f g a subtree) (foldtree f g a rest)

foldtree f g a Nil = a

我已经在Haskell中实现了这两种数据类型,而我目前正在尝试实施foldtree

data Listof a = Nil | Cons a (Listof a)
    deriving (Read, Show, Eq)

-- implementation of some list functions... (skipped)

data Treeof a = Node a (Listof (Treeof a))
    deriving (Read, Show, Eq)

foldtree f g a (Node label subtrees) = f label (foldtree f g a subtrees)
foldtree f g a (Cons subtree rest)   = g (foldtree f g a subtree) (foldtree f g a rest)
foldtree f g a Nil                   = a

但是我遇到了类型不匹配问题:

Couldn't match expected type ‘Treeof t’
            with actual type ‘Listof (Treeof t)’
Relevant bindings include
  subtrees :: Listof (Treeof t) (bound at whyFunMatters.hs:27:28)
  label :: t (bound at whyFunMatters.hs:27:22)
  f :: t -> t1 -> t1 (bound at whyFunMatters.hs:27:10)
  foldtree :: (t -> t1 -> t1)
              -> (t1 -> t1 -> t1) -> t1 -> Treeof t -> t1
    (bound at whyFunMatters.hs:27:1)
In the fourth argument of ‘foldtree’, namely ‘subtrees’
In the second argument of ‘f’, namely ‘(foldtree f g a subtrees)’

(等)

在考虑了foldtree的Hughes(伪)实现之后,我不太确定我理解它,那些类型不匹配现在对我来说显而易见。更具体地说,foldtree的第四个参数的类型在这三种模式中看起来并不一致:

  • 在第一个模式中,该参数的类型为Treeof a,而
  • 在最后两种模式中,它的类型为Listof (Treeof a)

我错过了什么?

3 个答案:

答案 0 :(得分:7)

正确的定义应该包含一对相互递归的函数,一个用于折叠,另一个用于折叠森林(树列表):

foldtree :: (a -> c -> b) -> (b -> c -> c) -> c -> Treeof a -> b
foldtree f g a (Node label subtrees) = f label (foldforest f g a subtrees)

foldforest :: (a -> c -> b) -> (b -> c -> c) -> c -> Listof (Treeof a) -> c
foldforest f g a (Cons subtree rest) = g (foldtree f g a subtree) (foldforest f g a rest)
foldforest f g a Nil                 = a

我认为作者错误地将两个不同(但密切相关)的功能组合在一起。我怀疑作者写的不是真正的Haskell,而是更像是类似Haskell的伪代码,所以代码只是用来以非正式的方式呈现算法。

请注意,该论文似乎暗示它是Haskell的前身Miranda,但我无法确认这是否是合法的Miranda代码。

答案 1 :(得分:1)

Rufflewind的回答是解决Hughes着名论文中foldtree可疑定义的最明显方法。然而,有一个更加简洁和模块化的解决方案,只需要一行代码并重用foldr。滚动到此帖子的底部以查看此定义。继续阅读以获得某种定义的推导。

比较Rufflewind对foldforest的定义:

foldforest f g a (Cons subtree rest) = g (foldtree f g a subtree) (foldforest f g a rest)
foldforest f g a Nil                 = a

......来自论文的foldr的Hughes(略有变化)定义:

foldr f a (Cons e rest)              = f e (foldr f a rest)
foldr f a Nil                        = a

他们俩看起来都不太相似吗?实际上,唯一的区别是在foldforest中我们将foldtree f g a应用于subtree并且(通过递归)将子树列表中的所有其他元素应用于{{1} }}。我们可以定义一个执行此操作的运算符,并且可以传递给g吗?

让我们仔细看看实际工作完成的foldr的核心:foldforest。使用函数组合(在Hughes的论文中定义为g (foldtree f g a subtree)),我们可以用不同的方式表达这一部分:

(f . g) h = f (g h)

现在让我们定义foldforest f g a (Cons subtree rest) = (g . foldtree f g a) subtree (foldforest f g a rest) 以简洁并将一个具体的子树列表传递给h = (g . foldtree f g a),使用我们的新表示法展开递归:

foldforest

展开对foldforest f g a (Cons subtree1 (Cons subtree2 Nil)) = h subtree1 (h subtree2 a) 的类似调用的递归是什么意思?

foldr

现在很明显我们能够从foldr f a (Cons subtree1 (Cons subtree2 Nil)) = f subtree1 (f subtree2 a) 中提取一个运算符,我们可以将其传递给foldforest以及起始状态foldra列表秒。 Treeof的完整定义,最大化模块化和简洁性,应该是:

foldtree

答案 2 :(得分:1)

在Google上搜索时,我发现链接https://gist.github.com/vu3rdd/14f10df24fbeffda09ae,作者通知该文档已更新,可以在http://www.cse.chalmers.se/~rjmh/Papers/whyfp.pdf上找到。

作者约翰·休斯(John Hughes)甚至为没有出现在Haskell上的示例道歉!

  

本文始于1984年,并作为查尔默斯的备忘录分发给许多人   年份。稍作修订的版本分别于1989年和1990年出现为[Hug90]   和[Hug89]。此版本基于原始的Chalmers备忘录nroff   来源,为LaTeX进行了轻松编辑,并将其与   已发布的版本,并且已纠正一两个错误。请   请原谅稍稍过时的字体设置,   例子不在Haskell中!

下面是作者提出的更正...

redtree f g a (node label subtrees) =
    f label (redtree’ f g a subtrees)
redtree’ f g a (cons subtree rest) =
    g (redtree f g a subtree) (redtree’ f g a rest)
redtree’ f g a nil = a