模式匹配数组与Either

时间:2012-09-22 05:06:19

标签: scala

说我有一些代码:

def foo(s:String):Either[Bar, Baz] = // some code here ... 

我想用它作为:

val a = Array("a", "b", "c")
a.map(foo) match {
   case something => // should match when all elements in array are of type Right
   case _ =>
}

任何人都可以为“某事”建议代码

编辑:喜欢直接匹配并使用Right[Bar,Baz]数组,而不是在匹配后提取。

3 个答案:

答案 0 :(得分:2)

使用forall方法检查数组中的所有元素是否为isRight

a.map(foo) match {
  case eithers if eithers.forall(_.isRight) =>
  case _ =>
}

关于你的评论,如果你希望匹配并在一次传递中转换为右边,那么尝试一个自定义提取器:

object RightArrayExtractor {
  def unapply(eithers: Array[Either[Bar, Baz]]) =
    eithers.foldLeft(Option(Vector[Right[Bar, Baz]]())) {
      case (Some(z), x @ Right(_)) => Some(z :+ x)
      case (None, _) => None
      case (_, Left(x)) => None
    }
}

a.map(foo) match {
  case RightArrayExtractor(eithers) => // eithers is a Vector[Right[Bar,Baz]]
  case _ =>
}

答案 1 :(得分:1)

您可以使用collect的便利性将数组更改为Right类型(无双关语)以及如果所有元素都为{{1,则收集的数组将具有相同的大小}}:

Right

答案 2 :(得分:1)

其他答案非常好,这个例子和其他答案一样不实用。我想补充一点基础理论。

您所描述的内容通常在函数式编程中称为遍历。你有一个像Seq[X]这样的集合和一个monadic(或者应用)计算X => M[Y]。标准map为您提供Seq[M[Y]],但遍历会为您提供M[Seq[Y]]

在这种情况下,monadic计算产生Either[Error,Right],在这种情况下M[_]Either[Error,_]。因此,如果您只使用此类函数映射集合,则会得到Seq[Either[Error,Right]]。但你想要的是Either[Error,Seq[Right]],这正是遍历所做的。如果函数在序列的任何元素上失败(返回Left(something)),那么最终结果就是Left(something)。如果函数对所有元素成功(对所有元素都返回Right(...)),则最终结果为Right(sequenceOfResults)

Scala没有内置函数,但Scalaz确实如此,它被称为traverse。一个完整的例子:

import scalaz._;
import Scalaz._;
import Applicative._;

object RightMatch extends App {
  // our example function
  def foo(s: String): Either[String,Int] =
    if (s.startsWith("a")) Right(s.length)
    else Left("wrong: " + s);

  // We make an utility function for traversing Sequences wit Eithers:
  def traverseRight[X,L,R](es: Seq[X], f: X => Either[L,R]): Either[L,Seq[R]] = {
    // we need to convert Either to Either.RightProjection
    type RightF[Y] = Either.RightProjection[L,Y];
    es.traverse[RightF,R](x => f(x).right).e; // and back to Either
  }

  // Or, if we just want to convert an existing sequence of eithers:
  def traverseRight[L,R](es: Seq[Either[L,R]]): Either[L,Seq[R]] =
    traverseRight(es, identity[Either[L,R]]);

  {
    val a = Seq("a", "ab", "ac");
    traverseRight(a, foo) match {
      case Right(arr) => println(arr); // we get the array of Ints here
      case Left(err)  => println(err); // we get String here (the first error)
    }
  }
}

(请注意,在Scalaz Array中没有可遍历的实现(我不知道为什么),所以我使用Seq代替。)

如上所述,遍历不仅适用于Either,它适用于任何monadic计算。因此,相同的方法可用于解决大量问题,例如对有状态计算进行排序(由scalaz的State建模),对非确定性计算进行排序(List monad)等。