Liftweb - конвертировать список [Box [T]] в Box [List [T]] - PullRequest
0 голосов
/ 15 марта 2012

Я хотел бы преобразовать List[Box[T]] в Box[List[T]].

Я знаю, что могу использовать foldRight, но не могу найти элегантный способ сделать это.Box, то есть, если есть какая-либо ошибка, вернуть Box с этой ошибкой.

Ответы [ 2 ]

2 голосов
/ 15 марта 2012

Если вы хотите собрать только «полные» значения

Я не уверен, почему вам нужен ящик [Список [T]], потому что пустого списка должно быть достаточно, чтобы сигнализировать об отсутствиилюбые значения.Я предполагаю, что это достаточно хорошо для вас.

У меня нет удобной копии Lift, но я знаю, что Box вдохновлен Option и имеет метод flatMap, поэтому:

Полная форма:

for {
  box <- list
  value <- box
} yield value

Более короткая форма:

list.flatMap(identity)

Кратчайшая форма:

list.flatten

Если вы также хотите собирать ошибки:

Вот функция mapSplit, которую я использую для решения подобных задач.Вы можете легко адаптировать его для использования Box вместо Either:

/**
 * Splits the input list into a list of B's and a list of C's, depending on which type of value the mapper function returns.
 */
def mapSplit[A,B,C](in: Traversable[A])(mapper: (A) ⇒ Either[B,C]): (Seq[B], Seq[C]) = {
  @tailrec
  def mapSplit0(in: Traversable[A], bs: Vector[B], cs: Vector[C]): (Seq[B], Seq[C]) = {
    in match {
      case t if t.nonEmpty ⇒
        val a = t.head
        val as = t.tail
        mapper(a) match {
          case Left(b)  ⇒ mapSplit0(as, bs :+ b, cs     )
          case Right(c) ⇒ mapSplit0(as, bs,      cs :+ c)
        }
      case t ⇒
        (bs, cs)
    }
  }

  mapSplit0(in, Vector[B](), Vector[C]())
}

И когда я просто хочу разделить что-то, что уже является Seq [Either [A, B]], я использую это:

/**
 * Splits a List[Either[A,B]] into a List[A] from the lefts and a List[B] from the   rights.
 * A degenerate form of {@link #mapSplit}.
 */
def splitEither[A,B](in: Traversable[Either[A,B]]): (Seq[A], Seq[B]) = mapSplit(in)(identity)
1 голос
/ 15 марта 2012

Это действительно проще сделать с помощью хвостовой рекурсивной функции, чем со складкой:

final def flip[T](l: List[Option[T]], found: List[T] = Nil): Option[List[T]] = l match {
  case Nil => if (found.isEmpty) None else Some(found.reverse)
  case None :: rest => None
  case Some(x) :: rest => flip(rest, x :: found)
}

Это работает как ожидалось:

scala> flip(List(Some(3),Some(5),Some(2)))
res3: Option[List[Int]] = Some(List(3, 5, 2))

scala> flip(List(Some(1),None,Some(-1)))
res4: Option[List[Int]] = None

Можно также сделать это с Iterator.iterate, но это более неловко и медленнее, поэтому в этом случае я бы избежал такого подхода.

(См. Также мой ответ в вопросе 4e6, связанном с.)

...