我有这样的模特:
case class Product(id:String, nextVersion:Option[Product] = None)
是否可以获得所有版本的产品?我可以使用while:
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"。
答案 0 :(得分:4)
我猜你的意思是你想要从产品链获得一系列ID,如下所示:
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
来创建它:创建的对象更少,最后不需要反向,如果调用者想要非顺序访问结果,则效率更高。但是让我们返回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
所做的那样)。