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)
。我错过了什么?
答案 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
以及起始状态foldr
和a
列表秒。 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