简体   繁体   English

树折操作?

[英]Tree Fold operation?

I am taking a class in Haskell, and we need to define the fold operation for a tree defined by: 我正在Haskell中学习一个类,我们需要为由以下定义的树定义折叠操作:

data Tree a = Lf a | Br (Tree a) (Tree a)

I can not seem to find any information on the "tfold" operation or really what it supposed to do. 我似乎无法找到关于“tfold”操作的任何信息,或者它真的应该做什么。 Any help would be greatly appreciated. 任何帮助将不胜感激。

I always think of folds as a way of systematically replacing constructors by other functions. 我总是认为折叠是一种通过其他函数系统地替换构造函数的方法。 So, for instance, if you have a do-it-yourself List type (defined as data List a = Nil | Cons a (List a) ), the corresponding fold can be written as: 因此,例如,如果您有自己动手的List类型(定义为data List a = Nil | Cons a (List a) ),则相应的折叠可以写为:

listfold nil cons Nil = nil
listfold nil cons (Cons a b) = cons a (listfold nil cons b)

or, maybe more concisely, as: 或者,可能更简洁,如:

listfold nil cons = go where
    go Nil = nil
    go (Cons a b) = cons a (go b)

The type of listfold is b -> (a -> b -> b) -> List a -> b . listfold的类型是b -> (a -> b -> b) -> List a -> b That is to say, it takes two 'replacement constructors'; 也就是说,需要两个“替代建设者”; one telling how a Nil value should be transformed into a b , another replacement constructor for the Cons constructor, telling how the first value of the Cons constructor (of type a ) should be combined with a value of type b (why b ? because the fold has already been applied recursively!) to yield a new b , and finally a List a to apply the whole she-bang to - with a result of b . 一个讲述一个怎样Nil值应转化为b ,另一个替代构造的Cons构造,告诉如何的第一个值Cons构造(类型a )应与类型的值相结合b (为什么b ?因为fold已经递归地应用!)产生一个新的b ,最后是一个List a来应用整个she-bang到 - 结果为b

In your case, the type of tfold should be (a -> b) -> (b -> b -> b) -> Tree a -> b by analogous reasoning; 在你的情况下, tfold的类型应该是(a -> b) -> (b -> b -> b) -> Tree a -> b通过类似的推理; hopefully you'll be able to take it from there! 希望你能从那里拿走它!

Imagine you define that a tree should be shown in the following manner, 想象一下,你定义一个树应该以下面的方式显示,

<1 # <<2#3> # <4#5>>>

Folding such a tree means replacing each branch node with an actual supplied operation to be performed on the results of fold recursively performed on the data type's constituents (here, the node's two child nodes, which are themselves, each, a tree), for example with + , producing 折叠这样的树意味着用对数据类型的成分(这里是节点的两个子节点,它们本身,每个,一个树) 递归执行的折叠结果执行实际提供的操作来替换每个分支节点,例如与+ ,生产

(1 + ((2+3) + (4+5)))

So, for leaves you should just take the values inside them, and for branches, recursively apply the fold for each of the two child nodes, and combine the two results with the supplied function, the one with which the tree is folded. 因此,对于叶子,您应该只取其中的值,对于分支,递归地为两个子节点中的每一个应用折叠, 并将两个结果与提供的函数(树折叠的函数) 组合 ( edit: ) When "taking" values from leaves, you could additionally transform them, applying a unary function. 编辑:)当从叶子“获取”值时,你可以另外转换它们,应用一元函数。 So in general, your folding will need two user-provided functions, one for leaves , Lf , and another one for combining the r esults of r ecursively folding the tree-like constituents (ie branches) of the branching nodes , Br . 因此,在一般情况下,你的折叠需要两个用户提供的功能,一个是Lf ,另一种用于组合为r的第r esults ecursively折叠分支节点的树状成分(即分行) Br

Your tree data type could have been defined differently, eg with possibly empty leaves, and with internal nodes also carrying the values. 您的树数据类型可能已被不同地定义,例如可能是空叶,并且内部节点也携带值。 Then you'd have to provide a default value to be used instead of the empty leaf nodes, and a three-way combination operation. 然后,您必须提供要使用的默认值而不是空叶节点,以及三向组合操作。 Still you'd have the fold defined by two functions corresponding to the two cases of the data type definition. 您仍然可以使用与数据类型定义的两种情况相对应的两个函数定义的折叠。

Another distinction to realize here is, what you fold, and how you fold it. 在这里实现的另一个区别是,你折的东西 ,你怎么把它折叠。 Ie you could fold your tree in a linear fashion, (1+(2+(3+(4+5)))) == ((1+) . (2+) . (3+) . (4+) . (5+)) 0 , or you could fold a linear list in a tree-like fashion, ((1+2)+((3+4)+5)) == (((1+2)+(3+4))+5) . 即你可以以线性方式折叠你的树, (1+(2+(3+(4+5)))) == ((1+) . (2+) . (3+) . (4+) . (5+)) 0 ,或者你可以用树状方式折叠线性列表, ((1+2)+((3+4)+5)) == (((1+2)+(3+4))+5) It is all about how you parenthesize the resulting "expression". 这完全取决于你如何将结果“表达式”括起来。 Of course in the classic take on folding the expression's structure follows that of the data structure being folded; 当然,在经典的折叠中 ,表达式的结构遵循折叠的数据结构; but variations do exist . 确实存在差异 Note also, that the combining operation might not be strict, and the " r esult" type it consumes/produces might express compound (lists and such), as well as atomic (numbers and such), values. 另请注意,组合操作可能不严格,并且它消耗/生成的“ r esult”类型可能表示复合 (列表等),以及原子(数字等)值。

(update 2019-01-26) This re-parenthesization is possible if the combining operation is associative, like + : (a 1 +a 2 )+a 3 == a 1 +(a 2 +a 3 ) . (更新2019-01-26)如果组合操作是关联的,则可以重新括号,如+ :( (a 1 +a 2 )+a 3 == a 1 +(a 2 +a 3 ) A data type together with such associative operation and a "zero" element ( a+0 == 0+a == a ) is known as "Monoid", and the notion of folding "into" a Monoid is captured by the Foldable type class. 数据类型连同这种关联操作和“零”元素( a+0 == 0+a == a )被称为“Monoid”,折叠“进入”Monoid的概念被Foldable类型捕获类。

A fold on a list is a reduction from a list into a single element. 列表中的折叠是从列表减少到单个元素。 It takes a function and then applies that function to elements, two at a time, until it has only one element. 它接受一个函数,然后将该函数一次两个地应用于元素,直到它只有一个元素。 For example: 例如:

Prelude> foldl1 (+) [3,5,6,7]
21

...is found by doing operations one-by-one: ...通过逐个操作找到:

3 + 5 == 8
8 + 6 == 14
14 + 7 == 21

A fold can be written 折叠可以写

ourFold :: (a -> a -> a) -> [a] -> a
ourFold _         [a]        = a -- pattern-match for a single-element list. Our work is done.
ourFold aFunction (x0:x1:xs) = ourFold aFunction ((aFunction x0 x1):xs)

A tree fold would do this, but move up or down the branches of the tree. 树折可以做到这一点,但可以向上或向下移动树的树枝。 To do this, it first need to pattern-match to see whether you're operating on a Leaf or a Branch. 要做到这一点,首先需要进行模式匹配,看看你是在Leaf还是Branch上运行。

treeFold _ (Lf a)   = Lf a -- You can't do much to a one-leaf tree
treeFold f (Br a b) = -- ...

The rest is left up to you, since it's homework. 剩下的由你来完成,因为它是家庭作业。 If you're stuck, try first thinking of what the type should be. 如果你遇到困难,首先要考虑应该是什么类型。

A fold is an operation which "compacts" a data structure into a single value using an operation. 折叠是使用操作将数据结构“压缩”为单个值的操作。 There are variations depending if you have a start value and execution order (eg for lists you have foldl , foldr , foldl1 and foldr1 ), so the correct implementation depends on your assignment. 根据您是否具有起始值和执行顺序(例如,对于具有foldlfoldrfoldl1foldr1 ),存在各种变化,因此正确的实现取决于您的分配。

I guess your tfold should simply replace all leafs with its values, and all branches with applications of the given operation. 我想你的tfold应该简单地用它的值替换所有叶子,并且用给定操作的应用程序替换所有分支。 Draw an example tree with some numbers, an "collapse" him given an operation like (+) . 用一些数字绘制一个示例树,给出像(+)这样的操作“折叠”他。 After this, it should be easy to write a function doing the same. 在此之后,编写一个相同的函数应该很容易。

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

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