在这个参数化函数中,为什么我需要演员?我怎么能摆脱它?
/** Filters `xs` to have only every nth element.
*/
def everyNth[A <% Iterable[B], B](xs: A, n: Int, offset: Int = 0): A =
(xs.zipWithIndex collect { case (x, i) if (i - offset) % n == 0 => x }).asInstanceOf[A]
如果我最后没有演员,我会收到以下错误消息:
type mismatch; found : Iterable[B] required: A
这个函数(使用强制转换)适用于我尝试过的所有情况,我知道在REPL中输入类似下面的内容,Scala能够在不在上下文中时正确推断结果类型参数化函数:
scala> val a: Stream[Int] = (Stream.from(0).zipWithIndex collect { case (x, i) if (i + 3) % 5 == 0 => x })
a: Stream[Int] = Stream(2, ?)
scala> a take 10 force
res20: scala.collection.immutable.Stream[Int] = Stream(2, 7, 12, 17, 22, 27, 32, 37, 42, 47)
请解释一下!
答案 0 :(得分:4)
根据评论中的一些建议,我查看了CanBuildFrom,这就是我想出的:
import scala.collection.IterableLike
import scala.collection.generic.CanBuildFrom
/** Filters `xs` to have only every nth element.
*/
def everyNth[A, It <: Iterable[A]]
(xs: It with IterableLike[A, It], n: Int, offset: Int = 0)
(implicit bf: CanBuildFrom[It, A , It]): It = {
val retval = bf()
retval ++= xs.zipWithIndex collect { case (x, i) if (i - offset) % n == 0 => x }
retval.result
}
是的,它有效!!!
还有 NO 强制转换。因此,它甚至适用于Ranges。
然而,不得不从一个空的retval开始,然后使用“++ =”来填充它似乎有点不优雅,所以如果有人有一个更优雅的解决方案,我都是耳朵。
这是我实现的另一个通用函数,它比上面有点棘手,因为返回类型与参数类型不同。即,输入是A
的序列,但输出是(A, A)
的序列:
def zipWithSelf[A, It[A] <: Iterable[A]]
(xs: It[A] with IterableLike[A, It[A]])
(implicit bf: CanBuildFrom[It[A], (A, A), It[(A, A)]]): It[(A, A)] = {
val retval = bf()
if (xs.nonEmpty) {
retval ++= xs zip xs.tail
retval.result
} else retval.result
}
这是另一个:
/** Calls `f(x)` for all x in `xs` and returns an Iterable containing the indexes for
* which `f(x)` is true.
*
* The type of the returned Iterable will match the type of `xs`.
*/
def findAll[A, It[A] <: Iterable[A]]
(xs: It[A] with IterableLike[A, It[A]])
(f: A => Boolean)
(implicit bf: CanBuildFrom[It[A], Int, It[Int]]): It[Int] = {
val retval = bf()
retval ++= xs.zipWithIndex filter { p => f(p._1) } map { _._2 }
retval.result
}
我仍然没有对“喜欢”类型和CanBuildFrom
有任何深刻的理解,但我得到了要点。在大多数情况下,将通用函数的转换版本编写为第一遍是很容易的,然后添加CanBuildFrom
和IterableLike
样板以使函数更通用且完全类型安全。< / p>
答案 1 :(得分:3)
在某些情况下,collect
不会返回与调用Iterable
相同的Range
子类型,例如在scala> everyNth(1 to 10, 2)
java.lang.ClassCastException: scala.collection.immutable.Vector cannot be cast to scala.collection.immutable.Range$Inclusive
at .<init>(<console>:9)
at .<clinit>(<console>)
at .<init>(<console>:11)
at .<clinit>(<console>)
at $print(<console>)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:704)
at scala.tools.nsc.interpreter.IMain$Request$$anonfun$14.apply(IMain.scala:920)
at scala.tools.nsc.interpreter.Line$$anonfun$1.apply$mcV$sp(Line.scala:43)
at scala.tools.nsc.io.package$$anon$2.run(package.scala:25)
at java.lang.Thread.run(Thread.java:679)
的情况下:
{{1}}
答案 2 :(得分:1)
问题在于,通过在xs上调用collect
,您可以将其转换为Iterable[B]
。 A <% Iterable[B]
表示A
可以被视为Iterable[B]
,这并不一定意味着Iterable[B]
也可以被视为A
。这里实际发生的是
def everyNth[A, B](xs: A, n: Int, offset: Int = 0)(implicit view: (A => Iterable[B])): A =
(view(xs).zipWithIndex collect {
case (x, i) if (i + offset) % n == 0 => x
}).asInstanceOf[A]
当我有这样的时候:
class Foo
implicit def foo2Iterable(foo: Foo) = List(foo)
并致电
everyNth(new Foo, 2)
我得到了
java.lang.ClassCastException: scala.collection.immutable.$colon$colon cannot be cast to Foo
你应该避免在这里施展。您可以从Iterable[B] => A
编辑:类型绑定在这里不起作用。