有时,我发现自己希望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
非常感谢!我不可能希望得到更全面和彻底的答案。我在这里学到了很多东西。 –