Map("name" -> "foo")
是一個函數調用,而不是一個構造函數,這意味着你不能寫:
Map("name" -> "foo") with MoreFilterOperations
更多,你可以寫
val m = Map("name" -> "foo")
val m2 = m with MoreFilterOperations
爲了得到一個mixin,你有使用具體類型,天真的第一次嘗試會是這樣的:
def EnhMap[K,V](entries: (K,V)*) =
new collection.immutable.HashMap[K,V] with MoreFilterOptions[(K,V)] ++ entries
在這裏使用工廠方法來避免重複類型參數。但是,這不起作用,因爲++
方法只是返回一個普通的舊HashMap
,沒有mixin!
解決方案(如Sam建議的)是使用隱式轉換來添加pimped方法。這將允許您使用所有常用技術轉換地圖,並且仍然可以在生成的地圖上使用您的額外方法。我通常做這個用的一類,而不是一個特質,具有構造PARAMS可導致一個更清晰的語法:
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
,因爲它嵌入了它的表示;如果沒有這個,filterFirstTwo
將返回一個Traversable
,無論啓動類型如何。
現在隱式轉換。這是類型表示法中事情有點棘手的地方。首先,我使用更高版本的類型來捕獲集合的表示形式:CC[X] <: Traversable[X]
,該參數表示CC
類型,該類型必須是Traversable的子類(請注意,使用X
作爲佔位符在這裏,CC[_] <: Traversable[_]
並不意味着一樣)。
還有一個隱含的CC[T] <:< TraversableLike[T,CC[T]]
,編譯器使用靜態保證我們收集CC[T]
是真正的TraversableLike
一個子類,因此對於MoreFilterOperations
構造一個有效的論據:
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)
}
到目前爲止,一切都很好。但是仍然有一個問題......它不適用於地圖,因爲它們需要兩個類型參數。解決的辦法是另一個隱含添加到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)
,但可以被看作就好像它們一樣。請記住MoreFilterOperations
構造函數中的Repr <% TraversableLike
?這是一個視圖綁定,並允許可以隱式轉換爲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
地圖進去,地圖出來。字符串進入,字符串出來。 etc ...
我還沒有嘗試過Stream
還有Set
或Vector
,但您可以確信,如果您這樣做了,它將返回與您開始使用的相同類型的集合。
謝謝!你說的Map工廠方法不是一個構造函數。經過一陣不安的睡眠,想知道Map [...,...]是否可以完成更多的篩選操作,我想我沒有再想那麼多了。 ;-)我會調整這個問題。 – 2011-02-17 13:42:13