有時,我發現自己希望scala集合包含一些缺失的功能,並且它很容易「擴展」集合,並提供自定義方法。泛型集合的生成與一般類型
這對於從頭開始構建集合有點困難。 考慮有用的方法,如.iterate
。 我將用類似的熟悉函數演示用例:unfold
。
unfold
是構造從初始狀態z: S
集合,以產生下一個狀態的一個可選的元組的功能,並且一個元件E
,或者指示結束的空選項的方法。
方法簽名,對於一些集合類型Coll[T]
應該大致如下:
def unfold[S,E](z: S)(f: S ⇒ Option[(S,E)]): Coll[E]
現在,國際海事組織,最 「自然」 的用法應該是,例如:
val state: S = ??? // initial state
val arr: Array[E] = Array.unfold(state){ s ⇒
// code to convert s to some Option[(S,E)]
???
}
這是相當直接做一個特定的收集類型:
implicit class ArrayOps(arrObj: Array.type) {
def unfold[S,E : ClassTag](z: S)(f: S => Option[(S,E)]): Array[E] = {
val b = Array.newBuilder[E]
var s = f(z)
while(s.isDefined) {
val Some((state,element)) = s
b += element
s = f(state)
}
b.result()
}
}
with t他在範圍內隱類,我們可以生成斐波那契序列是這樣一個數組:
val arr: Array[Int] = Array.unfold(0->1) {
case (a,b) if a < 256 => Some((b -> (a+b)) -> a)
case _ => None
}
但是,如果我們要提供這個功能給所有其他集合類型,我看比到C & P中的任何其他選擇代碼,並與List
,Seq
替換所有Array
出現,等等」 ......
所以,我想另一種方法:
trait BuilderProvider[Elem,Coll] {
def builder: mutable.Builder[Elem,Coll]
}
object BuilderProvider {
object Implicits {
implicit def arrayBuilderProvider[Elem : ClassTag] = new BuilderProvider[Elem,Array[Elem]] {
def builder = Array.newBuilder[Elem]
}
implicit def listBuilderProvider[Elem : ClassTag] = new BuilderProvider[Elem,List[Elem]] {
def builder = List.newBuilder[Elem]
}
// many more logicless implicits
}
}
def unfold[Coll,S,E : ClassTag](z: S)(f: S => Option[(S,E)])(implicit bp: BuilderProvider[E,Coll]): Coll = {
val b = bp.builder
var s = f(z)
while(s.isDefined) {
val Some((state,element)) = s
b += element
s = f(state)
}
b.result()
}
如今,隨着上述範圍,所有需要的是正確類型的進口產品:
import BuilderProvider.Implicits.arrayBuilderProvider
val arr: Array[Int] = unfold(0->1) {
case (a,b) if a < 256 => Some((b -> (a+b)) -> a)
case _ => None
}
但是這也不合適也。我不喜歡強迫用戶導入某些東西,更不用說在每個方法調用中都會創建一個無用的佈線類的隱式方法。而且,沒有簡單的方法來覆蓋默認邏輯。您可以考慮諸如Stream
之類的集合,其中最適合懶惰地創建集合,或考慮其他集合的其他特殊實現細節。
最好的解決方案,我可以想出,是採用第一種解決爲模板,併產生SBT來源:
sourceGenerators in Compile += Def.task {
val file = (sourceManaged in Compile).value/"myextensions"/"util"/"collections"/"package.scala"
val colls = Seq("Array","List","Seq","Vector","Set") //etc'...
val prefix = s"""package myextensions.util
|
|package object collections {
|
""".stripMargin
val all = colls.map{ coll =>
s"""
|implicit class ${coll}Ops[Elem](obj: ${coll}.type) {
| def unfold[S,E : ClassTag](z: S)(f: S => Option[(S,E)]): ${coll}[E] = {
| val b = ${coll}.newBuilder[E]
| var s = f(z)
| while(s.isDefined) {
| val Some((state,element)) = s
| b += element
| s = f(state)
| }
| b.result()
| }
|}
""".stripMargin
}
IO.write(file,all.mkString(prefix,"\n","\n}\n"))
Seq(file)
}.taskValue
但這種方法從其它問題受到影響,是難以維持。試想一下,如果unfold
不是全局添加的唯一函數,並且覆蓋默認實現仍然很困難。底線,這很難維持,也不「感覺」正確。
那麼,有沒有更好的方法來實現這一目標?
謝謝!關於'Stream',我對它並不是很熟悉,但也許有一種方法可以用'@ specialized'來增強你的解決方案嗎?(如果沒有太多要問,我很樂意看到一個可行的例子) –
@giladhoch我已經添加了一個例子,如何與'Stream's懶洋洋地合作 – Kolmar
非常感謝!我不可能希望得到更全面和徹底的答案。我在這裏學到了很多東西。 –