4

任意の Traversable[T] にミックスできる特性を作成したいとします。最後に、次のようなことが言えるようになりたいです。

val m = Map("name" -> "foo") with MoreFilterOperations

また、次のような Traversable が提供するもので表現される MoreFilterOperations のメソッドがあります。

def filterFirstTwo(f: (T) => Boolean) = filter(f) take 2

ただし、問題は明らかに、T が MoreFilterOperations の型パラメーターとして定義されていないことです。それをしたら、もちろん実行可能ですが、コードは次のようになります。

val m = Map("name" -> "foo") with MoreFilterOperations[(String,String)]

または、このタイプの変数を定義すると:

var m2: Map[String,String] with MoreFilterOperations[(String,String)] = ...

これは私の好みのために冗長にする方法です。後者を次のように記述できるように特性を定義したいと思います。

var m2: Map[String,String] with MoreFilterOperations

自己型、抽象型メンバーを試しましたが、何も役に立ちませんでした。手がかりはありますか?

4

3 に答える 3

11

Map("name" -> "foo")は関数呼び出しであり、コンストラクターではありません。これは、次のように記述できないことを意味します。

Map("name" -> "foo") with MoreFilterOperations

あなたが書くことができる以上

val m = Map("name" -> "foo")
val m2 = m with MoreFilterOperations

ミックスインを取得するには、具象型を使用する必要があります。単純な最初の試みは次のようになります。

def EnhMap[K,V](entries: (K,V)*) =
  new collection.immutable.HashMap[K,V] with MoreFilterOptions[(K,V)] ++ entries

ここでファクトリ メソッドを使用して、型パラメータを複製する必要がないようにします。ただし、メソッドはmixin なしで++単純な古い を返すだけなので、これは機能しません!HashMap

解決策 (サムが提案したように) は、暗黙的な変換を使用して pimped メソッドを追加することです。これにより、すべての通常の手法でマップを変換し、結果のマップで追加のメソッドを使用することができます。コンストラクターのパラメーターを使用できるようにすると、よりクリーンな構文になるため、通常はこれをトレイトではなくクラスで行います。

class MoreFilterOperations[T](t: Traversable[T]) {
  def filterFirstTwo(f: (T) => Boolean) = t filter f take 2
}

object MoreFilterOperations {
  implicit def traversableToFilterOps[T](t:Traversable[T]) =
    new MoreFilterOperations(t)
}

これにより、次のように書くことができます

val m = Map("name"->"foo", "name2"->"foo2", "name3"->"foo3")
val m2 = m filterFirstTwo (_._1.startsWith("n"))

しかし、コレクション フレームワークではまだうまく機能しません。マップから始めて、Traversable. それは物事がうまくいくはずの方法ではありません。ここでの秘訣は、高種類の型を使用してコレクション型も抽象化することです

import collection.TraversableLike

class MoreFilterOperations[Repr <% TraversableLike[T,Repr], T] (xs: Repr) {
  def filterFirstTwo(f: (T) => Boolean) = xs filter f take 2
}

十分に単純です。コレクションをRepr表す型である とT、要素の型である を指定する必要があります。その表現を埋め込むTraversableLike代わりに使用します。Traversableこれがないと、開始タイプに関係なくfilterFirstTwoa が返されます。Traversable

次に、暗黙の変換です。ここで、型表記がややこしくなります。まず、コレクションの表現をキャプチャするために高次の型を使用しています: CC[X] <: Traversable[X]、これは型をパラメーター化します。これCCは Traversable のサブクラスでなければなりません (Xここでのプレースホルダーとしてのの使用CC[_] <: Traversable[_]は同じことを意味しないことに注意してください)。

また、コンパイラがコレクションが本当に のサブクラスであり、コンストラクタの有効な引数であるCC[T] <:< TraversableLike[T,CC[T]]ことを静的に保証するために使用するimplicit もあります。CC[T]TraversableLikeMoreFilterOperations

object MoreFilterOperations {
  implicit def traversableToFilterOps[CC[X] <: Traversable[X], T]
  (xs: CC[T])(implicit witness: CC[T] <:< TraversableLike[T,CC[T]]) =
    new MoreFilterOperations[CC[T], T](xs)
}

ここまでは順調ですね。しかし、まだ 1 つの問題があります.2 つの型パラメーターを取るため、マップでは機能しません。MoreFilterOperations解決策は、以前と同じ原則を使用して、別の暗黙的なオブジェクトをオブジェクトに追加することです。

implicit def mapToFilterOps[CC[KX,VX] <: Map[KX,VX], K, V]
(xs: CC[K,V])(implicit witness: CC[K,V] <:< TraversableLike[(K,V),CC[K,V]]) =
  new MoreFilterOperations[CC[K,V],(K,V)](xs)

本当の美しさは、実際にはコレクションではないが、コレクションであるかのように表示できる型を操作したい場合に現れます。コンストラクターのRepr <% TraversableLikeを覚えていますか? MoreFilterOperationsこれはビュー バウンドであり、暗黙的に変換できる型とTraversableLike直接のサブクラスを許可します。文字列はこの典型的な例です:

implicit def stringToFilterOps
(xs: String)(implicit witness: String <%< TraversableLike[Char,String])
: MoreFilterOperations[String, Char] =
  new MoreFilterOperations[String, Char](xs)

REPL で実行する場合:

val m = Map("name"->"foo", "name2"->"foo2", "name3"->"foo3")
//  m: scala.collection.immutable.Map[java.lang.String,java.lang.String] =
//    Map((name,foo), (name2,foo2), (name3,foo3))

val m2 = m filterFirstTwo (_._1.startsWith("n"))
//  m2: scala.collection.immutable.Map[java.lang.String,java.lang.String] =
//    Map((name,foo), (name2,foo2))

"qaxfwcyebovjnbointofm" filterFirstTwo (_ < 'g')
//res5: String = af

地図が入り、地図が出てきます。弦が入り、弦が出ます。等...

、 、 、またはで試したことはStreamまだありませんが、試した場合は、最初に使用したのと同じタイプのコレクションが返されると確信できます。SetVector

于 2011-02-17T11:29:06.993 に答える
2

それはあなたが求めたものとはまったく異なりますが、この問題は暗黙的に解決できます。

trait MoreFilterOperations[T] {
  def filterFirstTwo(f: (T) => Boolean) = traversable.filter(f) take 2
  def traversable:Traversable[T]
}

object FilterImplicits {
  implicit def traversableToFilterOps[T](t:Traversable[T]) = new MoreFilterOperations[T] { val traversable = t }
}

object test {

  import FilterImplicits._

  val m = Map("name" -> "foo", "name2" -> "foo2", "name3" -> "foo3")
  val r = m.filterFirstTwo(_._1.startsWith("n"))
}

scala> test.r
res2: Traversable[(java.lang.String, java.lang.String)] = Map((name,foo), (name2,foo2))
于 2011-02-17T07:35:36.813 に答える
1

Scala標準ライブラリは、この目的のために暗黙を使用します。例"123".toInt:この場合の最善の方法だと思います。

それ以外の場合、不変のコレクションでは新しい混合クラスの新しいインスタンスを作成する必要があるため、「追加の操作を含むマップ」の完全な実装を実行する必要があります。

可変コレクションを使用すると、次のようなことができます。

object FooBar {
  trait MoreFilterOperations[T] {
    this: Traversable[T] =>
    def filterFirstTwo(f: (T) => Boolean) = filter(f) take 2
  }

  object moreFilterOperations {
    def ~:[K, V](m: Map[K, V]) = new collection.mutable.HashMap[K, V] with MoreFilterOperations[(K, V)] {
      this ++= m
    }
  }

  def main(args: Array[String]) {
    val m = Map("a" -> 1, "b" -> 2, "c" -> 3) ~: moreFilterOperations
    println(m.filterFirstTwo(_ => true))
  }
}

私はむしろ暗黙を使用したいと思います。

于 2011-02-17T10:28:11.220 に答える