失败解析视图

Failure parsing views

我在 Seq[Int] 上定义了以下 diff 函数,它使用 view 来避免复制数据:

object viewDiff {
  def main(args: Array[String]){
    val values = 1 to 10
    println("diff="+diffInt(values).toList)
  }

  def diffInt(seq: Seq[Int]): Seq[Int] = {
    val v1 = seq.view(0,seq.size-1)
    val v2 = seq.view(1,seq.size)
    (v2,v1).zipped.map(_-_)
  }
} 

此代码失败并显示 UnsupportedOperationException。如果我使用 slice 而不是 view 它会起作用。

谁能解释一下?

[使用 scala 2.10.5 和 2.11.6 测试]

编辑

我选择了 because it was the (first) correct explanation of the problem. However, 更详细,并提供了一个在压缩对象上使用视图的简单解决方案。

我还发布了适用于此特定案例的

备注

在上面的代码中,我在计算seq导数的算法上也犯了一个错误。最后一行应该是:seq.head +: (v2,v1).zipped.map( _-_ )

当您在代码中使用 seq.view 时,您正在创建 SeqView[Int, Seq[Int]] 个无法压缩的对象,因为它不支持 TraversableView.Builder.result。但是你可以使用这样的东西:

def diffInt(seq: Seq[Int]) = {
  val v1 = seq.view(0,seq.size-1)
  val v2 = seq.view(1,seq.size)

  (v2.toList,v1.toList).zipped.map {
    case (x1: Int, y1: Int) => x1-y1
    case _ => 0
  }
}

一样,压缩后无法使用视图。在我看来像是一个错误...

但这只有在第一个压缩序列是一个视图时才会发生。由于压缩在其任何序列完成时停止,因此可以将整个输入序列用作第一个压缩项目并反转 - 操作:

def diffInt2(seq: Seq[Int]): Seq[Int] = {
  val v1 = seq//.view(0,seq.size-1)
  val v2 = seq.view(1,seq.size)
  seq.head +: (v1,v2).zipped.map( (a,b) => b-a )  // inverse v1 and v2 order
}

这看起来确实很奇怪,zipped 似乎是罪魁祸首。你可以做的是,作为一个最小的变化,使用 zip:

def diffInt(seq: Seq[Int]): Seq[Int] = {
  val v1 = seq.view(0,seq.size-1)
  val v2 = seq.view(1,seq.size)
  v2.zip(v1).map { case (x1, x2) => x1 - x2 }
}

啊,命令式编程的那些美好时光:

val seq = 1 to 10
val i1 = seq.iterator
val i2 = seq.iterator.drop(1)
val i = scala.collection.mutable.ArrayBuffer.empty[Int]
while (i1.hasNext && i2.hasNext) i += i2.next - i1.next
println(i)

我想说它非常高效(没有复制和过度分配),而且可读性很好。

通常,您在 mapping 视图时不会构建视图,因为您想推迟构建结果集合,直到 force 视图。

由于 Tuple2Zipped 不是视图,因此它会尝试在地图上构建与其第一个元组集合(即视图)类型相同的结果。

SeqViewCanBuildFrom产生拒绝被强制的NoBuilder

由于使用Tuple2Zipped的目的是为了避免中间集合,你也想避免过早地强制,所以在映射之前先看一下:

scala> Seq(1,2,3).view(1,3)
res0: scala.collection.SeqView[Int,Seq[Int]] = SeqViewS(...)

scala> Seq(1,2,3).view(0,2)
res1: scala.collection.SeqView[Int,Seq[Int]] = SeqViewS(...)

scala> (res0, res1).zipped
res2: scala.runtime.Tuple2Zipped[Int,scala.collection.SeqView[Int,Seq[Int]],Int,scala.collection.SeqView[Int,Seq[Int]]] = (SeqViewS(...), SeqViewS(...)).zipped

scala> res2.view map { case (i: Int, j: Int) => i - j }
res3: scala.collection.TraversableView[Int,Traversable[_]] = TraversableViewM(...)

scala> .force
res4: Traversable[Int] = List(1, 1)

看一下机制:

import collection.generic.CanBuildFrom
import collection.SeqView
import collection.mutable.ListBuffer
import language._

object Test extends App {
  implicit val cbf = new CanBuildFrom[SeqView[Int, Seq[Int]], Int, Seq[Int]] {
    def apply(): scala.collection.mutable.Builder[Int,Seq[Int]] = ListBuffer.empty[Int]
    def apply(from: scala.collection.SeqView[Int,Seq[Int]]): scala.collection.mutable.Builder[Int,Seq[Int]] = apply()
  }

  //val res = (6 to 10 view, 1 to 5 view).zipped.map[Int, List[Int]](_ - _)
  val res = (6 to 10 view, 1 to 5 view).zipped.map(_ - _)
  Console println res
}