说我有一些代码:
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]
数组,而不是在匹配后提取。
答案 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)等。