繁体   English   中英

在Traversable视图上执行foldLeft时,在Scala中键入方差错误

[英]Type variance error in Scala when doing a foldLeft over Traversable views

我正在尝试使用foldLeft运算符连接Scala中的一系列Traversable视图,并且遇到了我不理解的类型方差错误。

我可以使用reduce来连接像这样的Traversable视图列表。

val xs = List(1,2,3,4).map(Traversable(_).view).reduce((a: TraversableView[Int, Traversable[_]], b: TraversableView[Int, Traversable[_]]) => a ++ b) // TraversableView[Int,Traversable[_]]
// xs.force returns Traversable[Int] = List(1, 2, 3, 4)

(注意,我必须在reduce参数上编写类型注释: reduce(_ ++ _)不编译。我不明白为什么,并且也会对此解释感激不尽。)

我也可以将列表分成头部和尾部并将它们连接起来。

import collection.TraversableView
val ns = List(1,2,3,4)
val h = Traversable(ns.head).view
val t = ns.tail.map(Traversable(_).view).reduce((a: TraversableView[Int, Traversable[_]], b: TraversableView[Int, Traversable[_]]) => a ++ b)
val xs = h ++ t // TraversableView[Int,Traversable[_]]
// xs.force returns Traversable[Int] = List(1, 2, 3, 4)

但是如果我尝试用foldLeft做同样的事情,我会得到类型方差错误。

import collection.TraversableView
val ns = List(1,2,3,4)
val h = Traversable(ns.head).view
val t = ns.tail.map(Traversable(_).view)
val xs = (h /: t)((a: TraversableView[Int, Traversable[_]], b: TraversableView[Int, Traversable[_]]) => a ++ b)
<console>:14: error: type mismatch;
 found   : scala.collection.TraversableView[Int,Traversable[_]]
 required: java.lang.Object with scala.collection.TraversableView[Int,Traversable[Int]]
              (h /: t)((a: TraversableView[Int, Traversable[_]], b: TraversableView[Int, Traversable[_]]) => a ++ b)

我怀疑这个问题与Traversable[_]的存在类型有关,但是我无法弄清楚究竟我做错了什么。 我在上面的表达式中尝试了各种类型签名,但都没有用。 从Stackoverflow上的其他问题来看, foldLeft的输入有些棘手,但我找不到解决这个问题的方法。

为了进行比较,使用Stream的相同算法可以毫无障碍地工作。

val xs = (1 #:: Stream.empty /: List(2,3,4).map(_ #:: Stream.empty))(_ ++ _)
// xs.force returns Stream[Int] = Stream(1, 2, 3, 4)

上面是我想要做的,除了我想使用视图而不是Stream因为我不需要记住我的所有结果。

这似乎是一个奇怪的请求。 我想以这种方式做事的原因是因为在Traversable视图上执行foldLeft提供了一种实现延迟深度优先搜索的有效方法。

这是一种解决方法(在2.9.2和2.10.0-RC2上测试):

import scala.collection.TraversableView

implicit def `I'm a lie!`[A]: collection.generic.CanBuildFrom[
  TraversableView[A, Traversable[A]], A, TraversableView[A, Traversable[A]]
] = null

val xs = List(1, 2, 3, 4).map(Traversable(_).view).reduce(_ ++ _)

哪个都编译并执行我们想要的操作:

scala> xs.toList
res0: List[Int] = List(1, 2, 3, 4)

即使使用(_ ++ _)语法,左折版本也将起作用。

问题是++方法需要一个隐式的CanBuildFrom实例,但实际上并没有使用它。 TraversableView对象提供了一个虚拟实例 ,但它是奇怪的类型(或者至少这种类型对我来说似乎很奇怪 - 也许对它有一个合理的解释)。

无论如何,至少到现在为止,将您自己类型适当的虚拟实例放入作用域中都是可行的。

视图不会跨++操作维护包含元素的标识。 我很确定这是一个错误。 您可以通过强制转换来修复它:

val xs = List(1,2,3,4).map(Traversable(_).view).
  reduce((a,b) => (a ++ b).asInstanceOf[TraversableView[Int,Traversable[Int]]])

在折叠案例中:

val xs = (Traversable(1).view /: List(2,3,4).map(x => Traversable(x).view)){ (l,r) =>
  (l ++ r).asInstanceOf[TraversableView[Int,Traversable[Int]]]
}

不过要小心! 一旦你开始明确地施放,你的类型安全性就会下降(即由你决定不犯错误)。

我猜这是一个没有被大量使用的观点的症状。 我通常尝试尽可能使用Iterator而不是各种不同的视图。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM