それは実際にsschaefは、それが任意にネストしたリストのために働くだろう提案しているという型クラスのアプローチのバージョンを書くには余りにも難しいことではありません。
trait Reverser[C] {
def reverse(xs: C): C
}
implicit def rev[A](implicit ev: Reverser[A] = null) = new Reverser[List[A]] {
def reverse(xs: List[A]) =
Option(ev).map(r => xs map r.reverse).getOrElse(xs).reverse
}
def deepReverse[A](xs: A)(implicit ev: Reverser[A]): A = ev.reverse(xs)
当社rev
方法で暗黙の引数ev
がA
そのものであるという証拠であります可逆であり、ev
がヌルの場合、そうでないことを意味します。 A
が可逆であるという証拠がある場合は、それを使用してList[A]
(これはmap
のことです)の要素を逆にしてから、リスト自体を逆にします。この証拠がない場合(getOrElse
の場合)、リストを逆にすることができます。
我々はこのように(おそらくもっとperformantlyけど)少し簡潔rev
を書くことができます:2つのバージョンのいずれかをテストするには
implicit def rev[A](implicit ev: Reverser[A] = null) = if (ev == null) {
new Reverser[List[A]] {
def reverse(xs: List[A]) = xs.reverse
}
} else {
new Reverser[List[A]] {
def reverse(xs: List[A]) = (xs map ev.reverse).reverse
}
}
、我々は次のように記述することができます。
scala> deepReverse(List.tabulate(3)(identity))
res0: List[Int] = List(2, 1, 0)
scala> deepReverse(List.tabulate(2,3) { case (a, b) => a + b })
res1: List[List[Int]] = List(List(3, 2, 1), List(2, 1, 0))
scala> deepReverse(List.tabulate(2, 3, 4, 5, 6) {
| case (a, b, c, d, e) => a + b + c + d + e
| }).head.head.head.head
res2: List[Int] = List(15, 14, 13, 12, 11, 10)
として、期待される。
私は、次は右のこのような場合には暗黙を取得するための、より一般的なイディオムであることを追加する必要があります:
trait ReverserLow {
implicit def listReverser[A] = new Reverser[List[A]] {
def reverse(xs: List[A]) = xs.reverse
}
}
object ReverserHigh extends ReverserLow {
implicit def nestedListReverser[A](implicit ev: Reverser[A]) =
new Reverser[List[A]] {
def reverse(xs: List[A]) = xs.map(ev.reverse).reverse
}
}
import ReverserHigh._
私達はちょうど同じレベルでlistReverser
とnestedListReverser
を書かたい場合は、
scala> deepReverse(List.tabulate(2, 3)(_ + _))
<console>:12: error: ambiguous implicit values:
both method listReverser...
and method nestedListReverser...
match expected type Reverser[List[List[Int]]]
deepReverse(List.tabulate(2, 3)(_ + _))
2つの優先順位を設定する標準的な方法では、優先順位の低いインプレッションを設定することができます形質(WhateverLow
)を付与し、他方は、その形質を延長する対象(WhateverHigh
)に付与する。しかし、このようなかなり単純なケースでは、上記の私のrev
メソッドでデフォルト引数のトリックを使用する方がより簡潔です(そして私の目には明確です)。しかし、他の人のコードで他のバージョンを見る可能性が高くなります。
くそー!私はそれ自体に型クラスを注入することを考えなかった。素晴らしいトリック! – sschaef
それは素晴らしいです! getOrElseは返されたマップがない場合は値自体を返します。とても有難い。 –
@GrittyKitty:ありがとう!ここでのトリックの詳細については、私のアップデートを参照してください。 –