OCaml中的fold_tree

发布于 2024-10-02 19:04:48 字数 454 浏览 4 评论 0原文

你可能知道,OCaml中有一些高阶函数,例如fold_left、fold_right、filter等。

在我的函数式编程课程中引入了名为fold_tree的函数,它类似于fold_left/right,不是在列表上,而是在(二元)树。它看起来像这样:

 let rec fold_tree f a t = 
  match t with
    Leaf -> a |
    Node (l, x, r) -> f x (fold_tree f a l) (fold_tree f a r);;

其中树定义为:

type 'a tree = 
  Node of 'a tree * 'a * 'a tree | 
  Leaf;;

好的,这是我的问题:fold_tree 函数如何工作?你能给我一些例子并用人类语言解释一下吗?

As You may know, there are higher order functions in OCaml, such as fold_left, fold_right, filter etc.

On my course in functional programming had been introduced function named fold_tree, which is something like fold_left/right, not on lists, but on (binary) trees. It looks like this:

 let rec fold_tree f a t = 
  match t with
    Leaf -> a |
    Node (l, x, r) -> f x (fold_tree f a l) (fold_tree f a r);;

Where tree is defined as:

type 'a tree = 
  Node of 'a tree * 'a * 'a tree | 
  Leaf;;

OK, here's my question: how does the fold_tree function work? Could you give me some examples and explain in human language?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

纸伞微斜 2024-10-09 19:04:48

这是一个样式建议,将条形放在行的开头。它使案件从哪里开始变得更清楚。为了保持一致性,第一个栏是可选的,但建议使用。

type 'a tree = 
  | Node of 'a tree * 'a * 'a tree
  | Leaf;;

let rec fold_tree f a t = 
    match t with
      | Leaf -> a
      | Node (l, x, r) -> f x (fold_tree f a l) (fold_tree f a r);;

至于它是如何工作的,请考虑以下树:

let t = Node(Leaf, 5, Node(Leaf, 2, Leaf));;

类型为 int tree

从视觉上看,t 看起来像这样:

   5
  / \
 ()  2
    / \
   () ()

调用fold_tree,我们需要一个函数来组合这些值。根据 Node 情况中的用法,f 采用 3 个参数,所有参数都与树的类型相同,并且返回相同的值。我们将这样做:

let f x l r = x + l + r;; (* add all together *)
fold_tree f 1 t;;

这将有助于了解每种情况下发生的情况。对于任何Leaf,都会返回 a。对于任何Node,它都会结合存储的值和左右子树折叠的结果。在本例中,我们只是将每片叶子算作 1 的数字相加。该树折叠的结果是10

Here's a style suggestion, put the bars on the beginning of the line. It makes it clearer where a case begins. For consistency, the first bar is optional but recommended.

type 'a tree = 
  | Node of 'a tree * 'a * 'a tree
  | Leaf;;

let rec fold_tree f a t = 
    match t with
      | Leaf -> a
      | Node (l, x, r) -> f x (fold_tree f a l) (fold_tree f a r);;

As for how it works, consider the following tree:

let t = Node(Leaf, 5, Node(Leaf, 2, Leaf));;

With the type int tree.

Visually, t looks like this:

   5
  / \
 ()  2
    / \
   () ()

And calling fold_tree, we'd need a function to combine the values. Based on the usage in the Node case, f takes 3 arguments, all the same type of the tree's and returns the same. We'll do this:

let f x l r = x + l + r;; (* add all together *)
fold_tree f 1 t;;

It would help to understand what happens in each case. For any Leaf, a is returned. For any Node, it combines the stored value and the result of folding the left and right subtrees. In this case, we're just adding the numbers where each leaf counts as one. The result on the folding of this tree is 10.

娇妻 2024-10-09 19:04:48

让我们举一个例子树。

let t = Node (Node (Leaf, 10, Leaf), 1, Node (Node (Leaf, 20, Leaf), 11, Leaf))

fold 操作的一般定义是用函数替换构造函数,树上到处都是。

general_fold_tree node leaf t =
    node (node leaf 10 leaf) 1 (node (node leaf 20 leaf) 11 leaf)

node 是一个函数,它从左侧 something、一个元素和右侧 something 构造一个 something,就像Node 是一个构造函数,它从左子树、节点内容和右子树构造一棵树。 leaf 是一个常量,与 Leaf 常量构造函数匹配。

let rec general_fold_tree (node : 'b -> 'a -> 'b -> 'b) (leaf : 'a) (t : 'a tree) : 'b =
  let recurse t = general_fold_tree node leaf t in
  match t with
  | Node (l, x, r) -> node (recurse l) x (recurse r)
  | Leaf -> leaf

请注意,函数的类型与类型定义的类型匹配,但类型定义描述 'a 树 的构建,而 Fold 函数描述任何 'b< 的构建/代码>。

看起来很像一般折叠的操作仍然称为折叠。例如,在列表上,List.fold_right 是根据上面定义的一般折叠; List.fold_left 是一种以相反方式应用该函数的变体(fold_left 相当于反向 + fold_right +反向,尽管它更清晰更有效率)。

您自己的 fold_tree 是此常规折叠的简单变体,其中节点函数以与构造函数不同的顺序获取参数:

let equrts_fold_tree f a t =
  let node l x r = f x l r in
  general_fold_tree node a t

Let's take an example tree.

let t = Node (Node (Leaf, 10, Leaf), 1, Node (Node (Leaf, 20, Leaf), 11, Leaf))

The general definition of a fold operation is that you replace constructors by functions, everywhere in the tree.

general_fold_tree node leaf t =
    node (node leaf 10 leaf) 1 (node (node leaf 20 leaf) 11 leaf)

node is a function that constructs a something from a left something, an element and a right something, just like Node is a constructor that constructs a tree from a left subtree, a node content and a right subtree. leaf is a constant, matching the Leaf constant constructor.

let rec general_fold_tree (node : 'b -> 'a -> 'b -> 'b) (leaf : 'a) (t : 'a tree) : 'b =
  let recurse t = general_fold_tree node leaf t in
  match t with
  | Node (l, x, r) -> node (recurse l) x (recurse r)
  | Leaf -> leaf

Notice that the types of the functions match those of the type definition, except that where the type definition describes the building of an 'a tree, the fold function describes the building of any 'b.

Operations that look a lot like the general fold are still called fold. For example, on lists, List.fold_right is the general fold according to the definition above; List.fold_left is a variation that applies the function the other way round (fold_left is equivalent to reverse + fold_right + reverse, though it's clearer and more efficient).

Your own fold_tree is a simple variation of this general fold where the node function takes its arguments in a different order from the constructor:

let equrts_fold_tree f a t =
  let node l x r = f x l r in
  general_fold_tree node a t
许你一世情深 2024-10-09 19:04:48

以下是考虑列表上的 fold_right 的方法:例如

(1 :: (2 :: (3 :: [])))

,使用一个新的二元操作代替 ::(以及一个新的常量代替 [])来重新解释该列表。 。

fold_right (+) l 0 = (1 + (2 + (3 + 0)))

如果您不想对列表执行任何操作,则可以将函数 cons 作为函数传递,将空列表作为中性元素传递。所以从某种意义上说,fold_right 非常通用:它甚至可以让你根本不丢失任何信息。

您问题中的 fold_tree 与树的数据类型的想法相同。如果你想重新解释你的树,你可以向它传递一个新的节点函数,而不是构造函数Node。如果您想获得一棵相同的树,可以将 Leaf 作为中性,将 (fun xlr -> Node (l, x, r)) 作为函数来应用。
与列表的 fold_left 类似,这个示例应用程序并不是很有趣,但这意味着 fold_left 是一个非常通用的转换(技术术语是 态射)。

例如,您还可以使用函数 (fun xlr -> x + l + r) 对树的元素求和。

Here is a way to think about fold_right on lists: a list is for instance

(1 :: (2 :: (3 :: [])))

and you re-interpret the list with a new binary operation in place of :: (and a new constant instead of []).

fold_right (+) l 0 = (1 + (2 + (3 + 0)))

If you wanted to do absolutely nothing to your list, you could pass the function cons as the function and the empty list as the neutral element. So in a sense, fold_right is very general: it even allows you not to lose any information at all.

The fold_tree in your question is the same idea for the datatype of trees. If you want to re-interpret your tree, you pass it a new function for nodes that will be applied instead of the constructor Node. If you wanted to get an identical tree, you could apply it with Leaf as neutral and (fun x l r -> Node (l, x, r)) as function.
Similarly to fold_left for lists, this example application is not very interesting, but it means that fold_left is a very general transformation (the technical term is morphism).

You can also sum the elements of the tree with the function (fun x l r -> x + l + r), for instance.

与风相奔跑 2024-10-09 19:04:48

看起来f是一个三参数约简函数,a是我们约简的中性元素,t是根,所以

:像这样的二进制文件(我不太记得变量类型的语法,所以请在这里居高临下)

let r = Node(Node(Node(Leaf,3,Leaf),2,Node(Leaf,4,Leaf)),1,Node(Node(Leaf,6,Leaf),5,Node(Leaf,7,Leaf)))

如果你想对所有节点求和,函数将被调用如下:

let add x y z = x + y + z
fold_tree add 0 r

我们会得到t 作为一个节点匹配,所以我们有:

(add 1 (fold_tree add 0 Node(Node(Leaf,3,Leaf),2,Node(Leaf,4,Leaf))) (fold_tree add 0 Node(Node(Leaf,6,Leaf),5,Node(Leaf,7,Leaf))))

如果我们再扩展一点,我们得到:

(add 1 (add 2 (fold_tree add 0 Node(Leaf,3,Leaf)) (fold_tree add 0 Node(Leaf,4,Leaf))) (add 5 (fold_tree add 0 Node(Leaf,6,Leaf)) (fold_tree add 0 Node(Leaf,7,Leaf))))

并且我们再次匹配叶子:

(add 1 (add 2 (add 3 0 0) (add 4 0 0)) (add 5 (add 6 0 0) (add 7 0 0))
(add 1 (add 2 3 4) (add 5 6 7))
(add 1 9 18)

最终得到:

28

希望它有帮助。

It seems that f is a three parameter reduction function, a is the neutral element of our reduction, and t is the root, so:

given a binary like (I don't remember very well the syntax of variant types, so please be condescendent here)

let r = Node(Node(Node(Leaf,3,Leaf),2,Node(Leaf,4,Leaf)),1,Node(Node(Leaf,6,Leaf),5,Node(Leaf,7,Leaf)))

if you want to sum all the nodes, the function will be called like:

let add x y z = x + y + z
fold_tree add 0 r

and we'll get t matched as a node, so we have:

(add 1 (fold_tree add 0 Node(Node(Leaf,3,Leaf),2,Node(Leaf,4,Leaf))) (fold_tree add 0 Node(Node(Leaf,6,Leaf),5,Node(Leaf,7,Leaf))))

if we expand it a little bit more, we get:

(add 1 (add 2 (fold_tree add 0 Node(Leaf,3,Leaf)) (fold_tree add 0 Node(Leaf,4,Leaf))) (add 5 (fold_tree add 0 Node(Leaf,6,Leaf)) (fold_tree add 0 Node(Leaf,7,Leaf))))

and once more, we are matching the leaves:

(add 1 (add 2 (add 3 0 0) (add 4 0 0)) (add 5 (add 6 0 0) (add 7 0 0))
(add 1 (add 2 3 4) (add 5 6 7))
(add 1 9 18)

to finally get:

28

Hope it helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文