需要将Seq [Option [A]]转换为Option [Seq [A]]

时间:2018-09-28 00:49:59

标签: scala fold scala-option

使用案例

我有一个文件列表,这些文件可能具有有效的mime类型。 在我的代码中,我使用Option来表示。

我需要将Seq [Option [T]]转换为Option [Seq [T]],以便在某些文件无效的情况下不处理列表。

错误

这是以下实现中的错误:

struct B {
    typedef void(A::*pointer)(int,int);
    pointer f;

    void set_handler(pointer p) { f = p; }
};

int main() {
    {
        B object1;
        object2.set_handler(&A::fun);
    }
    {
        B object2;
        object2.set_handler(&A::fun1);
    }
}

实施

boost::bind()

此实现返回found : (Option[Seq[A]], Option[A]) => Option[Seq[A]] [error] required: (Option[Any], Option[Any]) => Option[Any] [error] s.fold(init)(liftOptionItem[A]) 而不是def liftOptionItem[A](acc: Option[Seq[A]], itemOption: Option[A]): Option[Seq[A]] = { { acc match { case None => None case Some(items) => itemOption match { case None => None case Some(item) => Some(items ++ Seq(item)) } } } } def liftOption[A](s: Seq[Option[A]]): Option[Seq[A]] = { s.fold(Some(Seq()))(liftOptionItem[A]) } ,因为Option[Any]的类型不合适。

5 个答案:

答案 0 :(得分:1)

如果您使用TypeLevel Cats:

import cats.implicits._

List(Option(1), Option(2), Option(3)).traverse(identity)

返回:

Option[List[Int]] = Some(List(1, 2, 3))

您必须使用List,所以请先使用toList

Seq(Option(1), Option(2), Option(3)).toList.traverse(identity).map(_.toSeq)

答案 1 :(得分:1)

使用scalaz:

import scalaz._
import Sclaza._

val x:List[Option[Int]] = List(Option(1))
x.sequence[Option, Int] //returns Some(List(1))

val y:List[Option[Int]] = List(None, Option(1))
y.sequence[Option, Int] // returns None

答案 2 :(得分:0)

尾递归解决方案:如果seq元素中的任何一个为None,则返回None。

def seqToOption[T](s: Seq[Option[T]]): Option[Seq[T]] = {
  @tailrec
  def seqToOptionHelper(s: Seq[Option[T]], accum: Seq[T] = Seq[T]()): Option[Seq[T]] = {
    s match {
      case Some(head) :: Nil => Option(head +: accum)
      case Some(head) :: tail => seqToOptionHelper(tail, head +: accum)
      case _ => None
    }
  }
  seqToOptionHelper(s)
}

答案 3 :(得分:0)

以case语句处理None是代替Option[Seq[Any]] type返回Option[Seq[A]] type的原因。我们需要使功能 liftOptionItem[A]返回Option[Seq[Any]] type。并且可以通过对两个函数进行以下更改来解决编译错误。(由于fold的执行顺序没有特定限制,因此start value受到约束,因此return value受到限制。使用foldLeft代替fold。)

 def liftOptionItem[A](acc: Option[Seq[Any]], itemOption: Option[A]): Option[Seq[Any]] = {
     {
       acc match {
         case None => Some(Nil)
         case Some(items)=>
           itemOption match {
             case None => Some(items ++ Seq("None"))
             case Some(item) => Some(items ++ Seq(item))
           }
       }
     }
   }

 def liftOption[A](s: Seq[Option[A]]): Option[Seq[Any]] = {
   s.foldLeft(Option(Seq[Any]()))(liftOptionItem[A])
 }

现在,代码会编译。

在Scala REPL中:

scala> val list1 = Seq(None,Some(21),None,Some(0),Some(43),None)
list1: Seq[Option[Int]] = List(None, Some(21), None, Some(0), Some(43), None)

scala> liftOption(list1)
res2: Option[Seq[Any]] = Some(List(None, 21, None, 0, 43, None))

scala> val list2 = Seq(None,Some("String1"),None,Some("String2"),Some("String3"),None)
list2: Seq[Option[String]] = List(None, Some(String1), None, Some(String2), Some(String3), None)

scala> liftOption(list2)
res3: Option[Seq[Any]] = Some(List(None, String1, None, String2, String3, None))

答案 4 :(得分:0)

如果您不想使用cats或Scalaz之类的功能库,则可以使用foldLeft

def seqToOpt[A](seq: Seq[Option[A]]): Option[Seq[A]] =
    seq.foldLeft(Option(Seq.empty[A])){
        (res, opt) =>
          for {
            seq <- res
            v <- opt
          } yield seq :+ v
      }