Я предполагаю, что вы имеете в виду, что вы хотите получить 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
: создано меньше объектов, нет необходимости отменить в конце концов, и гораздо более эффективно, если абонент хочет получить доступ результата непоследовательно. Но давайте возвращать 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
).
Абсолютно неясно, что вы спрашиваете о – biesior
«Я могу использовать пока» - Возможно, проиллюстрируйте свой вопрос, показывая нам этот подход. –
вопрос обновлен –