我有一个这样的模型:
case class Product(id:String, nextVersion:Option[Product] = None)
是否可以获得所有版本的产品?我可以在以下时间使用:
def getAllVersions(product: Product) = {
var allVersions = List[Product]()
var actualProduct = product
while (actualProduct != null) {
allVersions :+= actualProduct
actualProduct = actualProduct.nextVersion.orNull
}
allVersions
}
但是也许有更好的方法可以做到这一点,更实用吗?
例如:
Product("1", Some(Product("2", Some(Product("3", None)))))
我想获取三个产品“ 1”,“ 2”,“ 3”的列表。
我猜您的意思是您想从产品链中获取ID的Seq,如下所示:
scala> case class Product(id:String, nextVersion:Option[Product] = None)
defined class Product
scala> val p = Product("1", Some(Product("2", Some(Product("3", None)))))
p: Product = Product(1,Some(Product(2,Some(Product(3,None)))))
scala> def toList(p:Product): List[String] = p match {
case Product(id, None) => List(id)
case Product(id, Some(next)) => id :: toList(next)
}
toList: (p: Product)List[String]
scala> toList(p)
res2: List[String] = List(1, 2, 3)
尾递归版本将更有效并且不受堆栈溢出的影响:
scala> def toList(p:Product) = {
@tailrec def loop(p: Product, soFar: List[String]): List[String] =
p match {
case Product(id, None) => id :: soFar
case Product(id, Some(next)) => loop(next, id :: soFar)
}
loop(p, Nil).reverse
}
toList: (p: Product)List[String]
scala> toList(p)
res4: List[String] = List(1, 2, 3)
尽管最好用以下方法来完成此任务Vector
:创建的对象更少,不需要在最后进行反转,并且如果调用者希望非顺序地访问结果,则效率更高。但是让我们返回a,Seq
以便我们以后可以更改类型:
scala> def toList(p:Product): Seq[String] = {
@tailrec def loop(p: Product, soFar: Vector[String]): Vector[String] =
p match {
case Product(id, None) => soFar :+ id
case Product(id, Some(next)) => loop(next, soFar :+ id)
}
loop(p, Vector())
}
toList: (p: Product)Seq[String]
scala> toList(p)
res5: Seq[String] = Vector(1, 2, 3)
以下版本较短,并且不是显式递归的-回叫toList
是从内部完成的Option#map
。但是,这当前意味着不会在递归上完成尾部调用优化(Scala编译器无法执行,而JVM当前不执行),因此如果产品版本链为,这可能会导致堆栈溢出足够长了。而且它的效率将低于上面的代码。
scala> def toList(p:Product): List[String] =
p.id :: (p.nextVersion map toList getOrElse Nil)
toList: (p: Product)List[String]
scala> toList(p)
res1: List[String] = List(1, 2, 3)
您可以定义和使用Iterator[Product]
,如下所示:
scala> implicit def productIterator(firstP: Product) = new Iterator[Product] {
private var optP = Option(firstP)
def hasNext = optP.isDefined
def next =
if (hasNext) {
val p = optP.get
optP = p.nextVersion
p
} else {
sys error "read past end of Iterator[Product]"
}
}
productIterator: (firstP: Product)Iterator[Product]
scala> (p map (_.id)).toList
res22: List[String] = List(1, 2, 3)
但是,由于您的数据结构是递归的,因此遍历它的代码必须是递归的(显式或隐式的)或使用可变变量(Iterator
确实如此)。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句