代码之家  ›  专栏  ›  技术社区  ›  Jus12

具有“任一”的模式匹配数组

  •  2
  • Jus12  · 技术社区  · 12 年前

    假设我有一些代码:

    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 回复  |  直到 12 年前
        1
  •  2
  •   dhg    12 年前

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

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

    关于你的评论,如果你想要一次匹配并转换为right all,那么尝试自定义提取器:

    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 _ =>
    }
    
        2
  •  1
  •   huynhjl bhericher    12 年前

    您可以使用 collect 将数组更改为 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 _ => 
    }
    
        3
  •  1
  •   Petr    12 年前

    其他的答案都很好,这个例子没有其他的那么实用。我只想补充一点基本理论。

    你所描述的通常被称为 遍历 在函数编程中。你有一个像 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) )那么最终结果就是这样 左(某物) .如果函数在所有元素上都成功(返回 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 没有traversable的实现(我不知道为什么),所以我使用 Seq 相反。)

    如前所述,遍历不仅适用于 Either s、 它适用于任何一元计算。因此,同样的方法可以用于广泛的问题,如排序有状态计算(由scalaz的 State ),排序非确定性计算( List monad)等。