更改 Scala 案例类树中的节点

发布于 2025-01-02 23:08:25 字数 322 浏览 1 评论 0原文

假设我有一些使用案例类构建的树,类似这样:

abstract class Tree
case class Branch(b1:Tree,b2:Tree, value:Int) extends Tree
case class Leaf(value:Int) extends Tree
var tree = Branch(Branch(Leaf(1),Leaf(2),3),Branch(Leaf(4), Leaf(5),6))

现在我想构建一个方法来将具有某个 id 的节点更改为另一个节点。很容易找到这个节点,但不知道如何更改。有什么简单的方法可以做到这一点吗?

Assume that I have some tree build using case classes, something like that:

abstract class Tree
case class Branch(b1:Tree,b2:Tree, value:Int) extends Tree
case class Leaf(value:Int) extends Tree
var tree = Branch(Branch(Leaf(1),Leaf(2),3),Branch(Leaf(4), Leaf(5),6))

And now I want to build a method to change the node with some id to another node. It's easy to find this node, but I don't know how to change it. Is there any simply way to do that?

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

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

发布评论

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

评论(3

死开点丶别碍眼 2025-01-09 23:08:25

这是一个非常有趣的问题!正如其他人已经指出的那样,您必须更改从根到要更改的节点的整个路径。不可变映射非常相似,您可能会学到一些查找的东西在 Clojure 的 PersistentHashMap 处。

我的建议是:

  • Tree 更改为 Node。您甚至在问题中将其称为“节点”,所以这可能是一个更好的名称。
  • value 拉至基类。您在问题中再次谈到了这一点,所以这可能是正确的地方。
  • 在您的替换方法中,请确保如果 Node 及其子节点均未更改,则不要创建新的 Node

注释在下面的代码中:

// Changed Tree to Node, b/c that seems more accurate
// Since Branch and Leaf both have value, pull that up to base class
sealed abstract class Node(val value: Int) {
  /** Replaces this node or its children according to the given function */
  def replace(fn: Node => Node): Node

  /** Helper to replace nodes that have a given value */
  def replace(value: Int, node: Node): Node =
    replace(n => if (n.value == value) node else n)
}

// putting value first makes class structure match tree structure
case class Branch(override val value: Int, left: Node, right: Node)
     extends Node(value) {
  def replace(fn: Node => Node): Node = {
    val newSelf = fn(this)

    if (this eq newSelf) {
      // this node's value didn't change, check the children
      val newLeft = left.replace(fn)
      val newRight = right.replace(fn)

      if ((left eq newLeft) && (right eq newRight)) {
        // neither this node nor children changed
        this
      } else {
        // change the children of this node
        copy(left = newLeft, right = newRight)
      }
    } else {
      // change this node
      newSelf
    }
  }
}

That's a very interesting question! As others have already noted, you have to change the entire path from root to the node you want to change. Immutable maps are very similar, and you may learn something looking at Clojure's PersistentHashMap.

My recommendations are:

  • Change Tree to Node. You even call it node in your question, so this is probably a better name.
  • Pull value up to the base class. Once again, you talk about that in your question, so this is probably the right place for it.
  • In your replace method, be sure that if neither a Node nor its children changes, don't create a new Node.

Comments are in the code below:

// Changed Tree to Node, b/c that seems more accurate
// Since Branch and Leaf both have value, pull that up to base class
sealed abstract class Node(val value: Int) {
  /** Replaces this node or its children according to the given function */
  def replace(fn: Node => Node): Node

  /** Helper to replace nodes that have a given value */
  def replace(value: Int, node: Node): Node =
    replace(n => if (n.value == value) node else n)
}

// putting value first makes class structure match tree structure
case class Branch(override val value: Int, left: Node, right: Node)
     extends Node(value) {
  def replace(fn: Node => Node): Node = {
    val newSelf = fn(this)

    if (this eq newSelf) {
      // this node's value didn't change, check the children
      val newLeft = left.replace(fn)
      val newRight = right.replace(fn)

      if ((left eq newLeft) && (right eq newRight)) {
        // neither this node nor children changed
        this
      } else {
        // change the children of this node
        copy(left = newLeft, right = newRight)
      }
    } else {
      // change this node
      newSelf
    }
  }
}
空气里的味道 2025-01-09 23:08:25

由于您的树结构是不可变的,因此您必须更改从节点到根的整个路径。
当您访问树时,保留访问过的节点的列表,然后使用复制方法 将所有节点更新到根正如 pr10001 所建议的。

As your tree structure is immutable, you have to change the whole path, from node up to the root.
when you visit your tree, keep a list of the visited nodes, then, update all the node up to the root, with the copy method, as suggested by pr10001.

复制方法:

val tree1 = Branch(Branch(Leaf(1),Leaf(2),3),Branch(Leaf(4), Leaf(5),6))
val tree2 = tree1.copy(b2 = tree1.b2.copy(b1 = Leaf(5))
// -> Branch(Branch(Leaf(1),Leaf(2),3),Branch(Leaf(5), Leaf(5),6))

The copy method:

val tree1 = Branch(Branch(Leaf(1),Leaf(2),3),Branch(Leaf(4), Leaf(5),6))
val tree2 = tree1.copy(b2 = tree1.b2.copy(b1 = Leaf(5))
// -> Branch(Branch(Leaf(1),Leaf(2),3),Branch(Leaf(5), Leaf(5),6))
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文