Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
此Scala模式匹配的简化或替代方案_Scala_Playframework 2.0_Pattern Matching - Fatal编程技术网

此Scala模式匹配的简化或替代方案

此Scala模式匹配的简化或替代方案,scala,playframework-2.0,pattern-matching,Scala,Playframework 2.0,Pattern Matching,我已经实现了一个剧本!2范围类型的Scala中的QueryStringBindable。范围由最小值或最大值或两者(类型为Float)组成。在我的QueryBindable实现中,我使用internalBinder将两个可能的参数min和max转换为Option[Orther[String,Float]],将它们组合成一个元组,在此基础上进行模式匹配,最后返回一个Option[Orther[String,Range]]。这是可行的,但正如您在下面的代码中看到的,模式匹配非常冗长。在Scala中有

我已经实现了一个剧本!2范围类型的Scala中的QueryStringBindable。范围由最小值或最大值或两者(类型为Float)组成。在我的QueryBindable实现中,我使用internalBinder将两个可能的参数min和max转换为Option[Orther[String,Float]],将它们组合成一个元组,在此基础上进行模式匹配,最后返回一个Option[Orther[String,Range]]。这是可行的,但正如您在下面的代码中看到的,模式匹配非常冗长。在Scala中有没有更简洁的方法? 也许利用高阶函数以某种方式获得相同的结果结构

import play.api.mvc.QueryStringBindable

case class Range(min: Option[Float], max: Option[Float])

object Range {

implicit def rangeQueryStringBindable(implicit intBinder: QueryStringBindable[Float]) = new QueryStringBindable[Range] {

    override def bind(key: String, params: Map[String, Seq[String]]): Option[Either[String, Range]] = {

      val minOpt = intBinder.bind("min", params)
      val maxOpt = intBinder.bind("max", params)
      (minOpt, maxOpt) match {
        case (None, None) => None
        case (Some(Right(min)), Some(Right(max))) => Some(Right(Range(Some(min), Some(max))))
        case (None, Some(Right(max))) => Some(Right(Range(None, Some(max))))
        case (Some(Right(min)), None) => Some(Right(Range(Some(min), None)))
        case (Some(Left(minError)), Some(Left(maxError))) => Some(Left(minError))
        case (Some(Left(minError)), None) => Some(Left(minError))
        case (None, Some(Left(maxError))) => Some(Left(maxError))
        case (Some(Right(_)), Some(Left(maxError))) => Some(Left(maxError))
        case (Some(Left(minError)), Some(Right(_))) => Some(Left(minError))
      }
    }

    override def unbind(key: String, range: Range): String = {
      (range.min, range.max) match {
        case (Some(min), Some(max)) => intBinder.unbind("min", min) + "&" + intBinder.unbind("max", max)
        case (Some(min), None) => intBinder.unbind("min", min)
        case (None, Some(max)) => intBinder.unbind("max", max)
        case (None, None) => throw new IllegalArgumentException("Range without values makes no sense")
      }
    }
  }
}

使用两个函数将
选项[Orther[Error,a]]
转换为
Orther[Error,Option[a]]]
,在我看来,您可以得到更干净的东西。我还建议重命名Range,因为它与
scala.collections.immutable
中同名的类冲突

import play.api.mvc.QueryStringBindable

case class RealRange(min: Option[Float], max: Option[Float])

object BindingEitherUtils {
  implicit class OptionWithEitherFlatten[A, B](value: Option[Either[A, B]]) {
    def flattenRight: Either[A, Option[B]] = {
      value.map { either =>
        either.right.map{ right => Some(right) }
      }.getOrElse{ Right(None) }
    }
  }

  implicit class EitherWithUnflatten[A, B](value: Either[A, Option[B]]) {
    def unflattenRight: Option[Either[A, B]] = {
      value.fold(left => Some(Left(left)), _.map{ right => Right(right) })
    }
  }
}

object RealRange {
  import BindingEitherUtils._

  val minError = "Invalid minimum value for RealRange"
  val maxError = "Invalid maximum value for RealRange"

  implicit def rangeQueryStringBindable(implicit floatBinder: QueryStringBindable[Float]) = new QueryStringBindable[RealRange] {
    override def bind(key: String, params: Map[String, Seq[String]]): Option[Either[String, RealRange]] = {
      val minOpt = floatBinder.bind("min", params).flattenRight
      val maxOpt = floatBinder.bind("max", params).flattenRight

      minOpt.left.map{ _ => minError }.right.flatMap { min =>
        maxOpt.left.map{ _ => maxError }.right.flatMap { max =>
          (min, max) match {
            case (None, None ) =>
              Right(None)
            case (Some(minVal), Some(maxVal)) if minVal > maxVal =>
              Left("Minimum value is larger than maximum value")
            case _ =>
              Right(Some(RealRange(min, max)))
          }
        }
      }.unflattenRight
    }

    override def unbind(key: String, range: RealRange): String = {
      (range.min, range.max) match {
        case (Some(min), Some(max)) => floatBinder.unbind("min", min) + "&" + floatBinder.unbind("max", max)
        case (Some(min), None) => floatBinder.unbind("min", min)
        case (None, Some(max)) => floatBinder.unbind("max", max)
        case (None, None) => throw new IllegalArgumentException("RealRange without values makes no sense")
      }
    }
  }

  def test(): Unit = {
    val binder = rangeQueryStringBindable

    Seq[(String, String)](
      ("10", "20"),
      ("10", null),
      (null, "10"),
      (null, null),
      ("asd", "asd"),
      ("10", "asd"),
      ("asd", "10"),
      ("asd", null),
      (null, "asd"),
      ("20", "10")
    ).foreach{ case (min, max) =>
      val params = Seq(
        Option(min).map{ m => "min" -> Seq(m) },
        Option(max).map{ m => "max" -> Seq(m) }
      ).flatten.toMap

      val result = binder.bind("", params)

      println(s"$params => $result" )
    }
  }
}
其结果是:

Map(min -> List(10), max -> List(20)) =>
  Some(Right(RealRange(Some(10.0),Some(20.0))))
Map(min -> List(10)) =>
  Some(Right(RealRange(Some(10.0),None)))
Map(max -> List(10)) =>
  Some(Right(RealRange(None,Some(10.0))))
Map() =>
  None
Map(min -> List(asd), max -> List(asd)) =>
  Some(Left(Invalid minimum value for RealRange))
Map(min -> List(10), max -> List(asd)) =>
  Some(Left(Invalid maximum value for RealRange))
Map(min -> List(asd), max -> List(10)) =>
  Some(Left(Invalid minimum value for RealRange))
Map(min -> List(asd)) =>
  Some(Left(Invalid minimum value for RealRange))
Map(max -> List(asd)) =>
  Some(Left(Invalid maximum value for RealRange))
Map(min -> List(20), max -> List(10)) =>
  Some(Left(Minimum value is larger than maximum value))

是的,可以简化

对于
bind
方法,当出现错误时,可以放置一些通配符来简化它。这样,对于
范围
汇编逻辑,您只有4个排列。我不会在这里施太多魔法,因为这会使理解代码变得复杂

override def bind(key: String, params: Map[String, Seq[String]]): Option[Either[String, Range]] = {

  val minOpt = intBinder.bind("min", params)
  val maxOpt = intBinder.bind("max", params)

  (minOpt, maxOpt) match {
    case (None, None) => None
    case (Some(Right(min)), Some(Right(max))) => Some(Right(Range(Some(min), Some(max))))
    case (None, Some(Right(max))) => Some(Right(Range(None, Some(max))))
    case (Some(Right(min)), None) => Some(Right(Range(Some(min), None)))
    // Error handling
    case (Some(Left(minError)), _) => Some(Left(minError))
    case (_, Some(Left(maxError))) => Some(Left(maxError))
  }
}
对于解除绑定,我将使用不同的方法,通过利用
选项的映射函数,然后将它们组合成
可编辑的
,您可以调用
mkString
,它对一个字符串不做任何操作,如果有两个字符串,则追加一个
&
。代码示例具有类型,因此您可以更容易地理解

def unbind(key: String, range: Range): String = {
  val minString: Option[String] = range.min.map(min => intBinder.unbind("min", min))
  val maxString: Option[String] = range.max.map(max => intBinder.unbind("max", max))
  val strings: Iterable[String] = minString ++ maxString
  strings match {
    case Nil => throw new IllegalArgumentException("Range without values makes no sense")
    case _ => strings.mkString("&")
  }
}
如果你喜欢短代码:

def unbind(key: String, range: Range): String = {
  val minString = range.min.map(min => intBinder.unbind("min", min))
  val maxString = range.max.map(max => intBinder.unbind("max", max))
  minString ++ maxString match {
    case Nil => throw new IllegalArgumentException("Range without values makes no sense")
    case strings => strings.mkString("&")
  }
}

谢谢那看起来确实简洁多了!就像你改进版的“解开”!但是对于“绑定”部分,@Marth的解决方案更短,可以节省2个案例。是的,我同意!我没有想到。有趣的方法,但就可读性而言,我更喜欢@Marth的解决方案。
(minOpt,maxOpt) match {
  case (None,None) => None
  case (Some(Left(m)),_) => Some(Left(m))
  case (_,Some(Left(m))) => Some(Left(m))
  case (_,_) => Some(Right(Range(minOpt.map(_.right.get),maxOpt.map(_.right.get))))
}