5

このパラメーター化された関数で、なぜキャストが必要なのですか?そして、どうすればそれを取り除くことができますか?

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

説明してください!

4

3 に答える 3

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     
}

はい、動作します!!!

そして、キャストはありません。そのため、範囲でも機能します。

ただし、空のretvalから始めて、それを埋めるために「++ =」を使用する必要があるのは少しエレガントではないように思われるので、誰かがよりエレガントな解決策を持っているなら、私はすべての耳です。

戻り型が引数型と同じではないため、上記よりも少しトリッキーな、私が実装した別のジェネリック関数を次に示します。つまり、入力は一連のA'sですが、出力は一連の(A, A)'sです。

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

于 2012-08-07T00:49:20.360 に答える
3

たとえば、:の場合など、呼び出されたcollectときと同じサブタイプを返さない場合があります。IterableRange

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)
于 2012-08-04T05:15:23.440 に答える
1

ここでの問題は、collectxsを呼び出すことにより、それをに変換すること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

編集:タイプバウンドはここでは機能しません。

于 2012-08-04T10:02:46.123 に答える