2012-08-04 78 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

相關問題