【问题标题】:How to make a class a member of two linked lists in Scala?如何使一个类成为Scala中两个链表的成员?
【发布时间】:2011-05-24 05:05:43
【问题描述】:

我觉得我在这里遗漏了一些非常明显的东西。

我正在将用 Java 编写的编译器生成器转换为 Scala 作为学习练习。

不是真的用Java写的,好像是C音译的。

在程序的一部分中,有节点。每个节点都是两个链表的一部分,并且有一些字段引用每个链表中的下一项(称为“交叉”和“向下”)。各种方法遍历这些列表,其中一个或两个,并对每个访问的节点执行操作。

我想使用 Scala 的集合而不是这些显式引用,因为有很多样板代码遍历并添加到这些列表中。我该怎么做呢?列表非常多变,所以我想使用可变列表

我想我不想要一个节点的链表,因为每个节点都需要知道它的下一个跨(或向下)邻居是什么,不管我是如何到达这个节点的,所以我需要能够从节点到任一列表。

我可以从 LinkedList 继承,但我需要这样做两次(一次用于交叉,一次用于向下)。

我可以有两个内部类(acrosslist 和 downlist),每个类都是 LinkedList 的实例,但它们可以是 LinkedList[Node] 吗?我无法理解这是如何工作的,因为列表的“下一个参考”需要是 node.acrosslist.next(比如说),而对于 LinkedList,它只是“下一个”。

所以,请指出明显的,或者如果不明显,我是如何让它工作的!

【问题讨论】:

    标签: scala linked-list scala-2.8


    【解决方案1】:

    让我稍微扩展一下Rex Kerranswer。 Scala 的所有集合实际上有三个中心类,其中只有两个是集合的一部分。它们是TraversableIterableIterator

    最基本的集合是Traversable。要成为Traversable,只有一个必要条件:它需要实现foreach 方法。因此,只要可以传递一个随后将应用于集合的每个元素的函数,它就可以是Traversable。例如:

    class Three[A](a: A, b: A, c: A) extends Traversable[A] {
        def foreach[U](f: (A) => U) {    
            f(a); f(b); f(c)
        }
    }
    

    这将为您提供所有Traversable 方法,尽管mapfilter 等方法不会返回Three,而是返回Traversable。让它返回您定义的类要困难得多,而且许多专门的类都做不到。例如,Three 不能这样做,因为删除了一些元素的filterThree 是什么?

    接下来是Iterator,它的作用与Traversable 几乎相同,但方式不同。 Iterator 必须定义两个方法:nexthasNext。例如:

    class Three[A](a: A, b: A, c: A) extends Iterator[A] {
        private var aReady, bIsRead, cIsRead = false
        def hasNext = !(aIsRead && bIsRead && cIsRead)
        def next = (aIsRead, bIsRead, cIsRead) match {
            case (false, _, _) => aIsRead = true; a
            case (_, false, _) => bIsRead = true; b
            case (_, _, false) => cIsRead = true; c
            case _ => Iterator.empty.next
        }
    }
    

    这将给出Iterator 的所有方法,它们看起来与Traversable 中的方法大部分相同。方法之间的区别主要与Iterator只能使用一次有关。

    最后是Iterable。要成为Iterable,一个类必须实现一个方法:iterator,它为该类返回一个Iterator。例如:

    class Three[A](a: A, b: A, c: A) extends Iterable[A] {
        // not necessary, but may offer a more efficient implementation
        override def foreach[U](f: (A) => U) {    
            f(a); f(b); f(c)
        }
    
        def iterator = new Iterator[A] {
            private var aReady, bIsRead, cIsRead = false
    
            def hasNext = !(aIsRead && bIsRead && cIsRead)
            def next = (aIsRead, bIsRead, cIsRead) match {
                case (false, _, _) => aIsRead = true; a
                case (_, false, _) => bIsRead = true; b
                case (_, _, false) => cIsRead = true; c
                case _ => Iterator.empty.next
            }
        }
    }
    

    所以,回到您的问题,您必须考虑您想要的预期行为是什么,以及您想要如何。特别是,Scala 集合中没有“向下”和“向左”的概念,这意味着 Node 可以有一个返回 Scala 集合的方法,但不能成为正确的说法,例如我们看到了 Rex Kerr 的解决方案。

    编辑

    让我举一个与 Rex Kerr 不同的例子。这里我做一个TraversableNode,遍历的顺序可以选择。

    class Node[A] extends Traversable[A] {
        var left: Node[A] = _
        var down: Node[A] = _
        var traverseLeft = true
        var value: A = _
    
        def foreach[U](f: (A) => U) = if (traverseLeft) foreachLeft(f) else foreachDown(f)
    
        def foreachLeft[U](f: (A) => U) { f(value); if (left != null) left.foreachLeft(f) }
        def foreachDown[U](f: (A) => U) { f(value); if (down != null) down.foreachDown(f) }
    }
    

    所以这个NodeTraversable,它支持所有的Traversable 方法(尽管它仍然不会从map 等返回Node,等等——查找有关此的其他问题)。您可以使用标志 (traverseLeft) 选择它是向左还是向下遍历,所有正常的 Traversable 方法都将使用调用该方法的节点中设置的任何内容。

    然而,这不是一个好的模型。我宁愿使用 Rex Kerr 的将迭代器返回到左和下的解决方案,或者完全离开 Scala 集合并使用Kiama 处理的东西。不过,后者是一个完全不同的范例,而不是你要硬塞进一个被音译为 Scala 的代码。

    【讨论】:

    • 谢谢。我需要再慢慢读一遍。然而,“一个节点可以有一个返回 Scala 集合的方法,但永远不可能是一个正确的方法,就像我们在 Rex Kerr 的解决方案中看到的那样。”。在这个应用程序中,大部分时间leftdown 是不相交的,因为我要么向下迭代,要么离开但不是混合。那么 Node 可以“成为”这两个集合中的每一个吗?我想你说不。因为像 iterator 这样的方法名称是硬连线的。
    • @Paul 是的,集合中有一个单一的遍历顺序,尽管它可以被切换。我会在答案中举一个例子。
    【解决方案2】:

    你为什么不自己把东西链接起来,然后在每个方向上创建迭代器?

    class Node(var left: Node, var down: Node) {
      def iterateDown = new Iterator[Node] {
        private[this] var current = this
        def hasNext = down ne null
        def next = { current = down; current }
      }
      def iterateLeft = new Iterator[Node] {
        private[this] var current = this
        def hasNext = left ne null
        def next = { current = left; current }
      }
    }
    

    【讨论】:

    • 当然可以。但是在每种情况下都有大量重复的代码——这就是我试图避免的。问题比我描述的要严重一些,因为可能有 6-10 个类,每个类至少有一个链表,所以至少我想将上述行为概括为一个特征 - 我还没有做到这一点尽管。我还希望能够用于理解、foreach 等(到目前为止,我发现有 findforeachforallexistscorresponds 的等价物)
    • 对于左/下,只需trait DoubleIterable[A] { var left: A; var down: A ; /* iterator defs here */ } 然后class Node extends DoubleIterable[Node]?
    • 是的。仍然比我想要的更多重复。
    • @Paul - 这里到底复制了什么?您的意思是您必须编写多个迭代器?会慢一点,但你可以先def makeIterator[A](getNext: => A, setNext: A => Unit) = { /* You fill this in */ },然后再trait DoubleIterable[A] { /*...*/ ; def iterateDown = makeIterator(left, left_= _) ; /*...*/ }
    • 是我必须编写的所有迭代器才能让 foreach/forall 等工作吗?或者我最终会得到它们的多个实现吗? Paul(目前正在尝试深入探索 Scala 的集合特性:))
    猜你喜欢
    • 1970-01-01
    • 2014-05-23
    • 1970-01-01
    • 1970-01-01
    • 2013-05-11
    • 1970-01-01
    • 2015-03-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多