2014-12-24 4 views
0

У меня есть такая модель:Получить все дочерние объекты в Scala

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".

+1

Абсолютно неясно, что вы спрашиваете о – biesior

+1

«Я могу использовать пока» - Возможно, проиллюстрируйте свой вопрос, показывая нам этот подход. –

+0

вопрос обновлен –

ответ

4

Я предполагаю, что вы имеете в виду, что вы хотите получить 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).

+0

Как насчет использования рекурсии? –

+0

Рекурсия - это основа функционального программирования. –

Смежные вопросы