2012-08-04 73 views
5

在这个参数化函数中,为什么我需要投射?我怎样才能摆脱它?在这个参数化的Scala函数中,为什么我需要投射?

/** 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

类似的问题,它使用'CanBuildFrom'来解决问题:[一般采用一种类型并返回相同类型的函数](http://stackoverflow.com/questions/10019529/function-which-generically-takes-一型和返回最相同类型)。我不能用它来解决这个问题,别人? – sschaef 2012-08-04 10:07:08

+0

我得到了CanBuildFrom来解决我的问题,并将解决方案放在答案中。如果您好奇,请参阅下面的答案。 – Douglas 2012-08-07 18:08:54

+0

好的答案!顺便说一句,你可以接受你自己的答案... – sschaef 2012-08-08 17:02:19

回答

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  
} 

耶,它的工作原理!

还有 cast。因此,它甚至适用于范围。

但是,不得不先从空的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,但我明白了。在大多数情况下,将通用函数的浇铸版本作为第一遍编写很容易,然后添加CanBuildFromIterableLike样板文件以使该函数更通用并且完全类型安全。

3

有些情况下,collect不以Range的情况下返回Iterable同一亚型因为它是所谓的,例如:在这里通过调用collect

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) 
+0

啊,当然。愚蠢的范围!是否还有其他可以用来排除这种不良行为的特征?或者我应该和演员一起生活? – Douglas 2012-08-04 05:25:19

+0

我想这样做的正确方法是使用集合API中使用的CanBuildFrom魔法? – 2012-08-04 07:32:12

+0

我得到了CanBuildFrom来解决我的问题,并将解决方案放在一个答案旁边。 – Douglas 2012-08-07 20:04:47

1

的问题是, xs您将其转换为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

编辑:类型绑定在这里不起作用。

+0

用绑定类型替换绑定的视图并没有摆脱这里需要的强制转换,所以我不清楚你所声称的是什么。 – Douglas 2012-08-06 19:08:24

+0

对不起,你是对的。从Iterable [B] => A添加视图将成为这里唯一的解决方案。 – drexin 2012-08-07 09:17:03

相关问题