2

说我有一些代码:

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]而不是在匹配后提取。

4

3 回答 3

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 _ =>
}
于 2012-09-22T05:10:25.633 回答
1

您可以方便collect地将数组更改为Right类型(没有双关语),并且如果所有元素都是,则收集的数组将具有相同的大小Right

a.map(foo).collect{case r@Right(_) => r} match {
  case a1 if a1.size == a.size =>
    // do something with a1 of type Array[Right[Bar,Baz]]
  case _ => 
}
于 2012-09-22T06:38:58.780 回答
1

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

您所描述的通常称为函数式编程中的遍历。你有一个像Seq[X]和一个单子(或应用)计算的集合X => M[Y]。标准map给你Seq[M[Y]],但遍历给你M[Seq[Y]]

在这种情况下,一元计算是产生 的东西Either[Error,Right],在这种情况下M[_]Either[Error,_]。所以如果你只是用这样一个函数映射一个集合,你会得到Seq[Either[Error,Right]]. 但你想要的是Either[Error,Seq[Right]],这正是遍历所做的。如果函数在序列的任何元素上失败(返回Left(something)),那么最终结果就是 this 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)
    }
  }
}

(请注意,在 ScalazArray中没有实现可遍历(我不知道为什么),所以我Seq改用了。)

如前所述,遍历不仅适用于Eithers,还适用于任何一元计算。因此,相同的方法可用于解决大量问题,例如对有状态计算进行排序(由 scalaz's 建模State)、对非确定性计算进行排序(Listmonad)等。

于 2012-09-24T08:10:18.460 回答