Список опций: эквивалент последовательности в Scala?
Что эквивалентно Haskell sequence
в Scala? Я хочу превратить список опций в список вариантов. Он должен выглядеть как None
, если любой из параметров None
.
List(Some(1), None, Some(2)).??? --> None
List(Some(1), Some(2), Some(3)).??? --> Some(List(1, 2, 3))
Ответы
Ответ 1
Если вы хотите, чтобы решение только для списка и варианта, а скорее общая монада, а затем выполнит задание,
def sequence[T](l : List[Option[T]]) =
if (l.contains(None)) None else Some(l.flatten)
REPL session,
scala> sequence(List(Some(1), None, Some(2)))
res2: Option[List[Int]] = None
scala> sequence(List(Some(1), Some(2), Some(3)))
res3: Option[List[Int]] = Some(List(1, 2, 3))
Обновление 20/8/2014
Просто используйте Scalaz...
Ответ 2
Scalaz определяет последовательность .
Вот пример:
scala> import scalaz._
import scalaz._
scala> import Scalaz._
import Scalaz._
scala> List(Some(1), None, Some(2)).sequence
res0: Option[List[Int]] = None
scala> List(some(1), some(2), some(3)).sequence
res1: Option[List[Int]] = Some(List(1, 2, 3))
Обратите внимание, что во втором примере вам нужно использовать некоторую функцию Scalaz для создания Some - в противном случае список создается как List [Some [Int]], что приводит к этой ошибке:
scala> List(Some(1), Some(2), Some(3)).sequence
<console>:14: error: could not find implicit value for parameter n: scalaz.Applicative[N]
List(Some(1), Some(2), Some(3)).sequence
Scalaz some (a) и никакие функции не создают значения Some и None типа Option [A].
Ответ 3
Вот те же функции, что и выше, используя комбинацию foldRight и map/flatmap, которая должна пройти только один раз:
def sequence[A](lo: List[Option[A]]): Option[List[A]] =
lo.foldRight (Option(List[A]())) { (opt, ol) =>
ol flatMap (l => opt map (o => o::l))
}
Или, если вы предпочитаете версию для понимания:
def sequence2[A](lo: List[Option[A]]): Option[List[A]] =
lo.foldRight (Option(List[A]())) { (opt, ol) =>
for {l <- ol; o <- opt} yield (o::l)
}
Ответ 4
Прежде всего, я рекомендую вам ознакомиться с API-документами для списка.
Что касается решения, это может быть не самый изящный способ сделать это, но он будет работать (и без внешних зависимостей):
// a function that checks if an option is a None
def isNone(opt:Option[_]) = opt match {
case None => true
case _ => false
}
//templated for type T so you can use whatever Options
def optionifyList[T](list:List[Option[T]]) = list.exists(isNone) match {
case true => None
case false => Some(list.flatten)
}
И тест, чтобы быть уверенным...
scala> val hasNone = Some(1) :: None :: Some(2) :: Nil
hasNone: List[Option[Int]] = List(Some(1), None, Some(2))
scala> val hasSome = Some(1) :: Some(2) :: Some(3) :: Nil
hasSome: List[Some[Int]] = List(Some(1), Some(2), Some(3))
scala> optionifyList(hasSome)
res2: Option[List[Int]] = Some(List(1, 2, 3))
scala> optionifyList(hasNone)
res3: Option[List[Int]] = None
Ответ 5
Это очень просто для понимания:
val x : Option[String] = Option("x")
val y : Option[String] = Option("y")
val z : Option[String] = None
// Result -> a: Option[List[String]] = None
val a = for {
x <- x
y <- y
z <- z
} yield List(x,y,z)
// Result -> b: Option[List[String]] = Some(List(x, y))
val b = for {
x <- x
y <- y
} yield List(x,y)
Ответ 6
Так как вам все равно нужно сгладить, просто сделайте это сначала...
def sequence(lo: List[Option[A]]): Option[List[A]] = lo.flatten match {
la: List[A] if(la.length == lo.length) => Some(la)
_ => None
}
хвостовая рекурсия может быть самой быстрой
Ответ 7
Может быть, это помогает, поскольку он проходит один раз и использует рекурсию
def sequence[A](a: List[Option[A]]): Option[List[A]] =
a match {
case Nil => Some(Nil)
case h :: rest => h.flatMap(x => sequence(rest).map(x :: _))
}