Преобразуйте Список Опций к Опции использования Списка Scalaz

Я хочу преобразовать a List[Option[T]] в a Option[List[T]]. Тип подписи функции

def lo2ol[T](lo: List[Option[T]]): Option[List[T]]

Ожидаемое поведение состоит в том, чтобы отобразить список, который содержит только Somes в a Some содержа список элементов в элементах Some. С другой стороны, если входной список имеет по крайней мере один None, ожидаемое поведение состоит в том, чтобы просто возвратиться None. Например:

scala> lo2ol(Some(1) :: Some(2) :: Nil)
res10: Option[List[Int]] = Some(List(1, 2))

scala> lo2ol(Some(1) :: None :: Some(2) :: Nil)
res11: Option[List[Int]] = None

scala> lo2ol(Nil : List[Option[Int]])
res12: Option[List[Int]] = Some(List())

Реализация в качестве примера, без scalaz, была бы:

def lo2ol[T](lo: List[Option[T]]): Option[List[T]] = {
  lo.foldRight[Option[List[T]]](Some(Nil)){(o, ol) => (o, ol) match {
    case (Some(x), Some(xs)) => Some(x :: xs);
    case _ => None : Option[List[T]]; 
}}}

Я не забываю видеть где-нибудь подобный пример, но использовать Scalaz для упрощения кода. Как это было бы похоже?


Немного больше сжатой версии, с помощью Scala2.8 PartialFunction.condOpt, но все еще без Scalaz:

import PartialFunction._

def lo2ol[T](lo: List[Option[T]]): Option[List[T]] = {
  lo.foldRight[Option[List[T]]](Some(Nil)){(o, ol) => condOpt(o, ol) {
    case (Some(x), Some(xs)) => x :: xs
  }
}}
28
задан Rafael de F. Ferreira 4 April 2010 в 19:36
поделиться

3 ответа

Есть функция, которая превращает Список [Вариант [A]] в Вариант [Список [A]] в Scalaz. Это последовательность . Чтобы получить Нет в случае, если какой-либо из элементов - Нет и Некоторый [Список [A]] , если все элементы - Некоторые ], вы можете просто сделать это:

import scalaz.syntax.traverse._
import scalaz.std.list._     
import scalaz.std.option._

lo.sequence

Этот метод фактически превращает F [G [A] в G [F [A]] , учитывая, что существует реализация ] Traverse [F] и Applicative [G] ( Option и List удовлетворяют обоим и предоставляются этим импортом).

Семантика Applicative [Option] такова, что если какой-либо из элементов списка List из Option имеет значение None , то последовательность также будет Нет . Если вы хотите получить список всех значений Some , независимо от того, являются ли другие значения None , вы можете сделать следующее:

lo flatMap (_.toList)

Вы можете обобщить это для любого Монада , которая также образует Моноид ( Список является одним из них):

import scalaz.syntax.monad._

def somes[F[_],A](x: F[Option[A]])
                 (implicit m: Monad[F], z: Monoid[F[A]]) =
  x flatMap (o => o.fold(_.pure[F])(z.zero))
21
ответ дан 28 November 2019 в 03:26
поделиться

Хотя Applicative[Option] в Scalaz имеет неправильное поведение для прямого использования MA#sequence, вы также можете вывести Applicative из Monoid. Это удобно делать с помощью MA#foldMapDefault или MA#collapse.

В данном случае мы используем моноид[Option[List[Int]]. Сначала мы выполняем внутреннюю карту (MA#∘∘), чтобы обернуть отдельные Ints в Lists из одного элемента.

(List(some(1), none[Int], some(2)) ∘∘ {(i: Int) => List(i)}).collapse assert_≟ some(List(1, 2))
(List(none[Int]) ∘∘ {(i: Int) => List(i)}).collapse                   assert_≟ none[List[Int]]
(List[Option[Int]]() ∘∘ {(i: Int) => List(i)}).collapse               assert_≟ none[List[Int]]

Абстрагируясь от List к любому контейнеру с экземплярами для Traverse, Pointed и Monoid:

def co2oc[C[_], A](cs: C[Option[A]])
                  (implicit ct: Traverse[C], cp: Pointed[C], cam: Monoid[C[A]]): Option[C[A]] =
  (cs ∘∘ {(_: A).pure[C]}).collapse


co2oc(List(some(1), none[Int], some(2)))   assert_≟ some(List(1, 2))
co2oc(Stream(some(1), none[Int], some(2))) assert_≟ some(Stream(1, 2))
co2oc(List(none[Int]))                     assert_≟ none[List[Int]]
co2oc(List[Option[Int]]())                 assert_≟ none[List[Int]]

К сожалению, попытка скомпилировать этот код в настоящее время либо вызывает #2741, либо отправляет компилятор в бесконечный цикл.

UPDATE Чтобы не обходить список дважды, я должен был использовать foldMapDefault:

(List(some(1), none[Int], some(2)) foldMapDefault (_ ∘ ((_: Int).pure[List])))

Этот ответ был основан на первоначальном запросе, что пустой список или список, содержащий только Noneы, должен возвращать None. Кстати, это лучше всего моделируется типом Option[scalaz.NonEmptyList] -- NonEmptyList гарантирует по крайней мере один элемент.

Если вам нужен просто List[Int], есть много более простых способов, приведенных в других ответах. Два прямых способа, которые не были упомянуты:

list collect { case Some(x) => x }
list flatten
2
ответ дан 28 November 2019 в 03:26
поделиться

По какой-то причине вам не нравится

if (lo.exists(_ isEmpty)) None else Some(lo.map(_.get))

? Это, наверное, самый короткий в Scala без Scalaz.

18
ответ дан 28 November 2019 в 03:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: