解析视图失败

时间:2016-03-02 13:41:41

标签: scala seq

我在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测试]

修改

我选择了Carlos's answer,因为它是(第一个)问题的正确解释。但是,som-snytt's answer更详细,并提供了一个使用压缩对象视图的简单解决方案。

我还发布了适用于此特定情况的very simple solution

注意

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

5 个答案:

答案 0 :(得分:2)

在代码中使用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
  }
}

答案 1 :(得分:2)

这看起来确实很奇怪,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 }
}

答案 2 :(得分:2)

通常,在map ping它们时,您不会构建视图,因为您希望推迟构建结果集合,直到force视图为止。

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

SeqView的{​​{1}}会产生拒绝强制的CanBuildFrom

由于使用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)

答案 3 :(得分:1)

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

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)

我说它有效率(没有复制和过多的分配),并且非常易读。

答案 4 :(得分:0)

作为Carlos Vilchez wrote,压缩无法使用视图。对我来说看起来像个错误......

但只有当第一个压缩的seq是视图时才会发生这种情况。由于zipped在任何seq完成时停止,因此可以使用整个输入seq作为第一个压缩项并反向 - 操作:

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
}