如何从Scala Iterable获取不同的项目,保持懒惰

时间:2013-04-24 16:15:22

标签: scala collections lazy-evaluation

我有一个java.lang.Iterable懒惰地计算其值。我从Scala访问它。是否存在仅返回不同值的核心API方式?例如,成像有一个过滤方法,也提供了迄今为止返回的所有结果:

val myLazyDistinctIterable = iterable.filter((previousReturnedItems, newItem) => previousReturnedItems.contains(newItem))

我想这不是一般情况,因为它涉及存储以前返回的项目,这可能就是它不在核心API中的原因。

我知道List.distinctSet但我想要的东西在被问到之前不会计算其元素。

5 个答案:

答案 0 :(得分:11)

您可以在Stream上使用distinct方法。例如,如果您有Iterable

val it = new java.lang.Iterable[Int] {
  def iterator = new java.util.Iterator[Int] {
    var i = 0
    var first = true

    def hasNext = true
    def next =
      if (first) { first = false; i } else { first = true; i += 1; i - 1 }
    def remove() { throw new UnsupportedOperationException("Can't remove.") }
  }
}

你可以写:

scala> import scala.collection.JavaConverters._
import scala.collection.JavaConverters._

scala> val s = it.asScala.toStream
s: scala.collection.immutable.Stream[Int] = Stream(0, ?)

scala> s.take(10).toList
res0: List[Int] = List(0, 0, 1, 1, 2, 2, 3, 3, 4, 4)

scala> val s = it.asScala.toStream.distinct
s: scala.collection.immutable.Stream[Int] = Stream(0, ?)

scala> s.take(10).toList
res1: List[Int] = List(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

我们可以判断一切都是懒惰的,因为流是无限的。

答案 1 :(得分:6)

更新仔细阅读问题很好。在这个解决方案中没有懒惰。遗憾。

toSet将完全符合您的要求:

  1. 将迭代元素存储在集合中(不是您想要的但需要的)
  2. 删除/替换重复项
  3. 实施例

    val it = Seq(1,2,3,4,2,4): Iterable[Int]
    it.toSet
    // Set(1,2,3,4)
    

    如果您觉得有点想象,可以将其转换回可迭代:

    it.toSet.toIterable
    

    或者,皮条客Iterable

    implicit class UniquableIterable[T](t: Iterable[T]) {
      def unique = t.toSet.toIterable
    }
    

    然后致电

    it.unique
    

答案 2 :(得分:1)

扩展我上面的评论,但我现在无法测试:

def unique[A](it: Iterator[A]): Iterator[A] = {
  val seen = mutable.Set[A]()
  it.filter { a =>
    if (seen(a))
      false
    else {
      seen += a
      true
    }
  }
}

至少你明白这个想法。然后,您可以将此应用于从迭代中获得的迭代器,而不会获得Stream的不必要的存储行为。

答案 3 :(得分:1)

以下是将.disctinct方法添加到Iterator的代码。

implicit class IteratorWrapper[T](it: Iterator[T]) {
    def distinct = new Iterator[T] {
        var seen = Set.empty[T]
        var ahead = Option.empty[T]

        def searchAhead {
            while (ahead.isEmpty && it.hasNext) {
                val v = it.next
                if (!seen(v)) {
                    seen += v
                    ahead = Some(v)
                }
            }
        }

        def hasNext = {
            searchAhead
            ahead.nonEmpty
        }

        def next = {
            searchAhead
            val result = ahead.get
            ahead = None
            result
        }
    }
}

请注意,对于迭代器通常如此,原始迭代器在调用.distinct后无效。

答案 4 :(得分:-1)

这应该做的工作(但我讨厌):

class UniqueIterable[T](i: Iterable[T]) extends Iterable[T] {
  import scala.collection.mutable.Set
  def iterator = new Iterator[T] {
    val it = i.iterator
    var nextE: Option[T] = None
    val seen: Set[T] = Set.empty
    def hasNext = {
      popNext()
      nextE.isDefined
    }
    def next = {
      popNext()
      val res = nextE.get
      nextE = None
      res
    }

    @tailrec
    private def popNext() {
      if (nextE.isEmpty && it.hasNext) {
        val n = it.next
        if (seen contains n) popNext()
        else {
          seen += n
          nextE = Some(n)
        }
      }
    }
  }
}