【问题标题】:Node based Iterable implementation in ScalaScala中基于节点的Iterable实现
【发布时间】:2018-10-31 05:15:06
【问题描述】:

通过 Sedgwick 和 Wayne 算法的练习继续我的旅行,我遇到了一个我必须实现 RandomBag 的算法。最初 RandomBag 应该实现 Iterable(在 java 中),它的 Iterator 必须以随机顺序提供项目。

这是我的 ImmutableRandomBag 的伴随对象:

object ImmutableRandomBag{
  case class Node[Item](item: Item, next: Option[Node[Item]])
  def apply[Item](maybeNode: Option[Node[Item]], size: Int): ImmutableRandomBag[Item] = new ImmutableRandomBag(maybeNode, size)
}

这是课程本身的开始:

class ImmutableRandomBag[Item](maybeNode: Option[Node[Item]], size: Int) extends Iterable[Item]{

      override def isEmpty: Boolean = size == 0

      def add(item: Item) = {
        ImmutableRandomBag(Some(Node(item, maybeNode)), size +1)
      }
   ...
   }

我的理解是 val size 应该覆盖了 Iterable 特征的 def 大小。在测试 add 方法时,我得到了 IndexOutOfBounException:

class RandomBagSpec extends BaseSpec {

  trait RandomBag{
    val begin = new ImmutableRandomBag[Connection](None, 0)
  }

  ...

  "Adding an item to empty RandomBag" should "return another bag with size 1" in new RandomBag {
    val bag = begin.add(Connection(0,1))
    bag.size should equal(1)
  }
}

虽然在构造函数参数中正确评估了调试大小,所以我不确定 IndexOutOfBoundException 来自哪里,但每当我调用 add 方法时都会得到它。也许问题源于以下。在 ImmutableRandomBag 中还有 Iterator 实现:

...
override def iterator: Iterator[Item] = new RandomIterator[Item](maybeNode)

  private class RandomIterator[Item](first: Option[Node[Item]]) extends Iterator[Item]{

    first match {
      case Some(node) => random(node)
      case None =>
    }

    var current: Int = 0
    var container: Vector[Item] = Vector()

    override def hasNext: Boolean = current < ImmutableRandomBag.this.size

    override def next(): Item = {
      val item = container(current)
      current += 1
      item
    }

    def random(first: Node[Item]) = {
      @tailrec
      def randomHelper(next: Option[Node[Item]], acc: List[Item]):List[Item]= next match {
        case None => acc
        case Some(node) => randomHelper(node.next, node.item::acc)
      }

      val items = randomHelper(Some(first), List[Item]())
      container = Random.shuffle(items).toVector
    }
  }
}

我在相同的规范中有一个不同的测试:

...
"Random Bag's iterator" should "contain all items passed to parent iterable" in new RandomBag{
    val connections = List(Connection(0,1), Connection(1,0), Connection(1,1))
    var localRB = begin
    for(c <- connections) localRB = localRB.add(c)
    assert(localRB.iterator.forall(conn=> connections.contains(conn)) == true)
  }
...

我还得到一个带有以下堆栈的 IndexOutOfBoundException:

    [info] RandomBagSpec:
[info] Random Bag's iterator
[info] - should contain all items passed to parent iterable *** FAILED ***
[info]   java.lang.IndexOutOfBoundsException: 0
[info]   at scala.collection.immutable.Vector.checkRangeConvert(Vector.scala:123)
[info]   at scala.collection.immutable.Vector.apply(Vector.scala:114)
[info]   at ca.vgorcinschi.algorithms1_3_34.ImmutableRandomBag$RandomIterator.next(ImmutableRandomBag.scala:31)
[info]   at scala.collection.Iterator.forall(Iterator.scala:956)
[info]   at scala.collection.Iterator.forall$(Iterator.scala:954)
[info]   at ca.vgorcinschi.algorithms1_3_34.ImmutableRandomBag$RandomIterator.forall(ImmutableRandomBag.scala:18)
[info]   at ca.vgorcinschi.algorithms1_5_19.RandomBagSpec$$anon$1.<init>(RandomBagSpec.scala:16)
[info]   at ca.vgorcinschi.algorithms1_5_19.RandomBagSpec.$anonfun$new$1(RandomBagSpec.scala:12)
[info]   at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
[info]   at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)

问题似乎来自调用迭代器的下一个方法,实际上容器Vector 不包含任何元素:

但是为什么next 在random 之前被调用?

【问题讨论】:

    标签: scala iterator nodes iterable


    【解决方案1】:

    val size 应该覆盖了 Iterable trait 的 def size

    val 会有,但你没有;您在非case 类中只有一个构造函数参数。实际上它是一个private val 并且不能覆盖任何东西。

    但是为什么 next 在 random 之前被调用?

    不是;在RandomIterator 的构造函数中,random 在初始化器container = Vector() 之前被调用(作为first match ... 的一部分)。 next 仅在构造函数之后调用。

    【讨论】:

    • 谢谢 Alexey,在 size 前面加上 override val 修复了第一个问题。你是对的,之前输入了随机数,items 正确地获取了所有传入的项目,但是,不知何故,如果在我的测试中以调试模式突出显示localRB.iterator,container 的大小为 0(基本上是在从构造函数中退出时) ?
    • 我现在明白了 - 它后来被 =Vector() 覆盖。但是我该怎么做呢?将容器声明更改为val container: Vector[Item] = Vector(),在模式匹配之前将其向上移动并使用container ++Random.shuffle(items).toVector 向其中添加混洗项目会引发相同的异常。
    • 呜呼我想通了。我必须将它保留为 var 但只是在模式匹配之前移动它!再次感谢!
    • 就个人而言,我会让random返回向量而不是修改var,那么它可以是val container: Vector[Item] = first match …。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-24
    • 1970-01-01
    • 2010-11-06
    • 2016-10-21
    • 2011-08-28
    • 1970-01-01
    • 2014-01-09
    相关资源
    最近更新 更多