List of options: equivalent of sequence in Scala?

后端 未结 7 601
别那么骄傲
别那么骄傲 2021-01-04 04:55

What is the equivalent of Haskell\'s sequence in Scala? I want to turn list of options into an option of list. It should come out as None if any of the options

相关标签:
7条回答
  • 2021-01-04 05:32

    Here is the same function as above using a combination of foldRight and map/ flatmap that only has to traverse the list once:

      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))
        }
    

    Or, if you prefer the for comprehension version:

      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)
        }
    
    0 讨论(0)
  • 2021-01-04 05:36

    Scalaz defines sequence.

    Here's an example:

    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))
    

    Note that in the second example, you have to use Scalaz's some function to create a Some -- otherwise, the List is constructed as List[Some[Int]], which results in this error:

    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
    

    The Scalaz some(a) and none functions create Some and None values of type Option[A].

    0 讨论(0)
  • 2021-01-04 05:37

    Since you need to flatten anyway, just do it first...

    def sequence(lo: List[Option[A]]): Option[List[A]] = lo.flatten match {
        la: List[A] if(la.length == lo.length) => Some(la)
        _ => None
    }
    

    tail recursion might be quickest

    0 讨论(0)
  • 2021-01-04 05:46

    First off, I recommend that you check out the API docs for List.

    As for a solution, this may not be the most graceful way to do it, but it'll work (and with no external dependencies):

    // 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)
    }
    

    And a test just to be sure...

    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
    
    0 讨论(0)
  • 2021-01-04 05:48

    This is very simple with a for comprehension:

    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)
    
    0 讨论(0)
  • 2021-01-04 05:50

    If you want a solution for just List and Option rather a general monad then following will do the job,

    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)) 
    

    Update 20/8/2014

    Just use Scalaz ...

    0 讨论(0)
提交回复
热议问题