如何通过索引获取子树? [英] How do I get a subtree by index?

查看:43
本文介绍了如何通过索引获取子树?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有以下树:

在我的程序中,这棵树用一个列表表示:'(+ (* 5 6) (sqrt 3)).

In my program, this tree is represented using a list: '(+ (* 5 6) (sqrt 3)).

如何通过索引获取子树?

How do I get a subtree by its index?

索引应该从 0 开始并且深度优先.在上图中,我用索引标记了所有节点以显示这一点.

The index should start from 0 and be depth-first. In the picture above, I have labelled all the nodes with their index to show this.

例如:

(define tree '(+ (* 5 6) (sqrt 3)))

(subtree tree 0)  ; Returns: '(+ (* 5 6) (sqrt 3)))
(subtree tree 1)  ; Returns: '(* 5 6)
(subtree tree 2)  ; Returns: 5
(subtree tree 3)  ; Returns: 6
(subtree tree 4)  ; Returns: '(sqrt 3)
(subtree tree 5)  ; Returns: 3

我尝试像这样实现subtree:

(define (subtree tree index)
  (cond [(= index 0) tree]
        [else
         (subtree (cdr tree)
                  (- index 1))]))

然而,这不会遍历子列表.这是不正确的.

However, this does not traverse into sublists. It is incorrect.

我尝试使用延续传递风格实现subtree:

I tried to implement subtree using continuation-passing style:

(define (subtree& exp index counter f)
  (cond [(= counter index) exp]
        [(null? exp) (f counter)]
        [(list? exp)
         (let ((children (cdr exp)))
           (subtree& (car children)
                     index
                     (+ counter 1)
                     (lambda (counter2)
                       (if (null? (cdr children))
                           (f counter)
                           (subtree& (cadr children)
                                     index
                                     (+ counter2 1)
                                     f)))))]
        [else (f counter)]))

(define (subtree tree index)
  (subtree& tree
            index
            0
            (lambda (_)
              (error "Index out of bounds" index))))

这适用于如下树:

  • '(+ 1 2)
  • '(+ (* 5 6) (sqrt 3))

但是,它对于如下树失败了:

However, it fails for trees like:

  • '(+ 1 2 3)

我的实现有什么问题?

推荐答案

无需繁琐的控制结构即可做到这一点的方法是制定议程.

The way to do this without hairy control constructs is with an agenda.

但在此之前,定义抽象.每次我看到正在行走的代码,它称为树",并且充满了明确的carcdr &c 我不得不阻止自己简单地冷-启动宇宙,希望我们能得到更好的.如果教你的人没有告诉你这个对他们说狠话.

But before you do that, define abstractions. Every time I look at code which is walking something it calls a 'tree' and is full of explicit car, cdr &c I have to stop myself from simply cold-booting the universe in the hope we get a better one. If whoever is teaching you is not telling you this have strong words with them.

这里是树结构的一些抽象.这些特别重要,因为树结构真的很不规则:我希望能够在任何节点上说给我这个节点的孩子":叶子只是没有孩子的节点,不是某种特殊的东西.

Here are some abstractions for the tree structure. These are particularly important because the tree structure is really irregular: I want to be able to say 'give me the children of this node' on any node: leaves are just nodes with no children, not some special kind of thing.

(define (make-node value . children)
  ;; make a tree node with value and children
  (if (null? children)
      value
      (cons value children)))

(define (node-value node)
  ;; the value of a node
  (if (cons? node)
      (car node)
      node))

(define (node-children node)
  ;; the children of a node as a list.
  (if (cons? node)
      (cdr node)
      '()))

现在对议程进行一些抽象.议程表示为列表,但当然没有其他人知道这一点,而且更具工业实力的实施可能不希望这样表示.

Now some abstractions for the agenda. Agendas are represented as lists, but nothing else knows this of course, and a more industrial-strength implementation might well not want to represent them like that.

(define empty-agenda
  ;; an empty agenda
  '())

(define agenda-empty?
  ;; is an agenda empty?
  empty?)

(define (agenda-next agenda)
  ;; return the next element of an agenda if it is not empty
  ;; error if it is
  (if (not (null? agenda))
      (car agenda)
      (error 'agenda-next "empty agenda")))

(define (agenda-rest agenda)
  ;; Return an agenda without the next element, or error if the
  ;; agenda is empty
  (if (not (null? agenda))
      (cdr agenda)
      (error 'agenda-rest "empty agenda")))

(define (agenda-prepend agenda things)
  ;; Prepend things to agenda: the first element of things will be
  ;; the next element of the new agenda
  (append things agenda))

(define (agenda-append agenda things)
  ;; append things to agenda: the elements of things will be after
  ;; all elements of agenda in the new agenda
  (append agenda things))

现在可以轻松编写函数的纯迭代版本(议程是维护堆栈),无需各种繁琐的控制结构.

Now it's easy to write a purely iterative version of the function (the agenda is maintaining the stack), without all sorts of hairy control constructs.

(define (node-indexed root index)
  ;; find the node with index index in root.
  (let ni-loop ([idx 0]
                [agenda (agenda-prepend empty-agenda (list root))])
    (cond [(agenda-empty? agenda)
           ;; we're out of agenda: raise an exception
           (error 'node-indexed "no node with index ~A" index)]
          [(= idx index)
           ;; we've found it: it's whatever is next on the agenda
           (agenda-next agenda)]
          [else
           ;; carry on after adding all the children of this node
           ;; to the agenda
           (ni-loop (+ idx 1)
                    (agenda-prepend (agenda-rest agenda)
                                    (node-children
                                     (agenda-next agenda))))])))


需要考虑的事情:如果在上述函数中将 agenda-prepend 替换为 agenda-append 会发生什么?


A thing to think about: what happens if you replace agenda-prepend by agenda-append in the above function?

这篇关于如何通过索引获取子树?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆