Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cassandra/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Tree 如何用指定索引处的另一棵树替换树的一部分?_Tree_Scheme_Racket - Fatal编程技术网

Tree 如何用指定索引处的另一棵树替换树的一部分?

Tree 如何用指定索引处的另一棵树替换树的一部分?,tree,scheme,racket,Tree,Scheme,Racket,假设我有两棵树: 树A-”(+(*56)(sqrt 3)): 树B-”(-42): 目标:在指定的树A索引位置用树B替换树A的子树之一。索引位置从根节点的0开始,并且是深度优先。在上面树A的图中,我用它们的索引标记了所有节点,以显示这一点 例如,(replace subtree treeA 4 treeB)将树A中索引4处的子树替换为树B,从而生成树(+(*5 6)(-4 2)): 如何实现(替换子树树a索引树b) 这个问题与我的另一个问题有些关联:。我在解决这个问题上有很大的困

假设我有两棵树:

  • 树A-
    ”(+(*56)(sqrt 3))

  • 树B-
    ”(-42)

目标:在指定的树A索引位置用树B替换树A的子树之一。索引位置从根节点的0开始,并且是深度优先。在上面树A的图中,我用它们的索引标记了所有节点,以显示这一点

例如,
(replace subtree treeA 4 treeB)
将树A中索引4处的子树替换为树B,从而生成树
(+(*5 6)(-4 2))

如何实现
(替换子树树a索引树b)


这个问题与我的另一个问题有些关联:。我在解决这个问题上有很大的困难,但我最终通过使用连续传递方式(CPS)找到了一个可行的解决方案。然而,这个问题似乎要困难得多。我完全不知道该如何开始!实现和线索是最受欢迎的。我对不使用
call/cc
的实现特别感兴趣


编辑

在等待答案时,我想出了一个权宜之计。它依赖于
set,我不喜欢

(define (replace-subtree tree index replacement)
  (define counter 0)
  (define replaced #f)  ; Whether or not something has been replaced.

  (define (out-of-bounds-error)
    (error "Index out of bounds" index))

  (define (traverse-tree tree)
    (cond [(null? tree)
           (error "Invalid tree: ()")]
          [(= counter index)
           (set! counter (+ counter 1))
           (set! replaced #t)
           replacement]
          [(pair? tree)
           (set! counter (+ counter 1))
           (cons (car tree)
                 (traverse-children (cdr tree)))]
          [else
           ;; Possible only during the initial call to traverse-tree.
           ;; e.g. (replace-subtree 'not-a-list 9999 '(+ 1 2)) -> error.
           (out-of-bounds-error)]))

  (define (traverse-children children)
    (cond [(null? children) '()]
          [(list? (car children))
           ;; list? instead of pair? to let traverse-tree handle invalid tree ().
           (cons (traverse-tree (car children))
                 (traverse-children (cdr children)))]
          [(= counter index)
           (set! counter (+ counter 1))
           (set! replaced #t)
           (cons replacement
                 (traverse-children (cdr children)))]
          [else
            (set! counter (+ counter 1))
            (cons (car children)
                  (traverse-children (cdr children)))]))

  (let ([result (traverse-tree tree)])
   (if replaced
       result
       (out-of-bounds-error))))

这是一个比我想象的更难的问题。这很困难的一个原因是,你称之为“树”的东西实际上不是树:它们是DAG(有向无环图),因为它们可以共享子树。简单地说,这只发生在叶节点上:在
(abb)
中,索引为1和2的节点是
eq?
:它们是相同的对象。但事实上,它可以发生在任何节点上:给定

(define not-a-tree
  (let ([subtree '(x y)])
    (list 'root subtree subtree)))
索引为1和2的节点是同一对象,不是叶节点:这是DAG,不是树

这很重要,因为它破坏了一个显而易见的方法:

  • 找到你感兴趣的索引节点
  • 使用节点上的
    eq?
    ,遍历构建新树的树,直到找到此节点,然后将其替换
  • 您可以看到,如果我想用
    (x y y)
    中的索引2替换节点,那么这将失败:它将用索引1替换节点

    一种可能是最简单的方法是将这些“树”转化为节点具有标识的树。然后对这些树执行上述替换,然后将它们转换回原始表示。然而,这可能会丢失一些重要的结构:例如,上面的对象将从DAG变成树。这在实践中不太重要

    因此,要做到这一点,你需要一个函数来获取老树,将它们转换为具有适当唯一性的新树,然后将它们转换回来。这几乎可以肯定是概念上最简单的方法,但我太懒了,没有写出所有这些代码

    所以,这里有一个答案,不是那种方法。相反,它所做的是遍历树,跟踪节点索引,并在需要时构建新树。要做到这一点,进入节点的东西需要返回两个东西:一个节点(可能是新创建的节点,即替换节点或传递的原始节点)和索引的新值。这是通过从walker返回两个值来完成的,并且有相当数量的头发在执行此操作

    这也没有尝试使用Racket的一些小子集:它使用多个值,包括语法(
    let values
    ),这使得它们使用起来不那么痛苦,并且还使用
    for/fold
    来完成大部分工作,包括折叠多个值。所以,你需要了解这些事情,看看它能做什么。(这也可能意味着它不适合作为家庭作业的答案。)

    有一点值得注意的是,walker有点作弊:一旦完成了替换,它甚至都不会尝试正确地计算索引:它只知道它比它所关心的要大,然后逃之夭夭

    首先,这里是处理树的抽象:请注意,
    makenode
    与前一个问题的答案中的
    makenode
    并不完全相同:它现在需要一个孩子列表,这是一个更有用的签名

    (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
      (cond
        [(cons? node)
         (car node)]
        [else
         node]))
    
    (define (node-children node)
      ;; the children of a node as a list.
      (cond
        [(cons? node)
         (cdr node)]
        [else
         '()]))
    
    现在,这里是做这项工作的函数

    (define (replace-indexed-subtree tree index replacement)
      ;; Replace the subtree of tree with index by replacement.
      ;; If index is greater than the largest index in the tree
      ;; no replacemwnt will happen but this is not an error.
      (define (walk/indexed node idx)
        ;; Walk a node with idx.
        ;; if idx is less than or equal to index it is the index
        ;; of the node.  If it is greater than index then we're not
        ;; keeping count any more (as were no longer walking into the node).
        ;; Return two values: a node and a new index.
        (cond
          [(< idx index)
           ;; I still haven't found what I'm looking for (sorry)
           ;; so walk into the node keeping track of the index.
           ;; This is just a bit fiddly.
           (for/fold ([children '()]
                      [i (+ idx 1)]
                      #:result (values (if (< i index)
                                           node
                                           (make-node (node-value node)
                                                      (reverse children)))
                                       i))
                     ([child (in-list (node-children node))])
             (let-values ([(c j) (walk/indexed child i)])
               (values (cons c children) j)))]
          [(= idx index)
           ;; I have found what I'm looking for: return the replacement
           ;; node and a number greater than index
           (values replacement (+ idx 1))]
          [else
           ;; idx is greater than index: nothing to do
           (values node idx)]))
      ;; Just return the new tree (this is (nth-value 0 ...)).
      (let-values ([(new-tree next-index)
                    (walk/indexed tree 0)])
        new-tree))
    

    walk/index
    的顶部放置一个合适的
    printf
    ,这样你就可以看到它在树上行走时做了什么。

    ((lambda(a)(lambda(b)(+a(sqrt b)))1)4)
    树吗?它的
    汽车不也是一棵树吗?为什么忽视它?我认为它不应该被忽略。@WillNess
    '((lambda)(lambda(b)(+a(sqrt b)))1)4
    是一个具有根节点的树
    。((lambda(a)(lambda(b)(+a(sqrt b)))1
    和一个子节点
    4
    。在我的树实现中,列表的
    car
    是非终端节点的“内容”,列表的
    cdr
    包含其子节点。如果您的新实现工作正常,并且您希望对其进行审阅,那么是否可以将其发布到CodeReview?(不过那里的交通量很低…)。答案中的代码在AFAIAC中做了正确的事情,我也会做同样的事情,当然取决于语法。或者我们可以先运行上一个问题中的索引,将每个节点的有效负载与其索引配对。我认为@tfb在最初的讨论中暗示了这一点。然后,在这种索引树上,替换代码将在日志时间内直接到达所需节点,而不必像它那样在线性时间内搜索整个树。索引本身当然是线性的,即树中节点和叶子的数量是线性的。同样,我认为这只适用于dfs索引,而不适用于bfs索引。谢谢您的回答。请给我几天时间运行、调整并理解您的答案,然后再投票/接受它。(在阅读了您答案的第一部分后)re
    eq?> (replace-indexed-subtree '(+ (* 5 6) (sqrt 3)) 4 '(- 4 2))
    '(+ (* 5 6) (- 4 2))
    > (replace-indexed-subtree '(+ (* 5 6) (sqrt 3)) 0 '(- 4 2))
    '(- 4 2)
    > (replace-indexed-subtree '(+ (* 5 6) (sqrt 3)) 20 '(- 4 2))
    '(+ (* 5 6) (sqrt 3))