【问题标题】:Idiomatic scala for getting a single option out of two Options and throwing exception if two are available用于从两个选项中获取单个选项并在两个可用时抛出异常的惯用 scala
【发布时间】:2014-09-26 17:40:09
【问题描述】:
val one: Option[Int] = None    
val two = Some(2)

Option(one.getOrElse(two.getOrElse(null))) // Gives me Some(2) which I want

val one = Some(1)
val two = None

Option(one.getOrElse(two.getOrElse(null))) // Gives me Some(1) which I want

val one: Option[Int] = None
val two: Option[Int] = None

Option(one.getOrElse(two.getOrElse(null))) // Gives me None which I want

val one = Some(1)
val two = Some(2)

Option(one.getOrElse(two.getOrElse(null))) // Gives me Some(1) when I want an exception

我简要研究了 Either 类型,但它似乎是用于“表示两种可能类型之一的值”。我是否缺少一些数据结构或 Monad?本质上,我想要一个明确的(如果两者都有价值,则抛出错误)如果可用,则获取其中一个或获取 None

【问题讨论】:

  • 你为什么不为此写一个函数,用模式匹配?
  • @gzou 我考虑过...我只是在检查我是否缺少预先构建的东西
  • 您可以将它们包装在一个集合中:List(Option(1), Option(2)).forall(_.isDefined) 如果它返回 true,则您的所有选项都已定义并且您可以抛出异常。
  • @EndeNeu 我回答了类似的问题。感谢您的建议
  • 永远不要使用 null。选项的全部意义在于避免 null 因为它更安全,如果 None 违背了类型的全部目的,则有一个选项返回 null

标签: scala monads scala-option


【解决方案1】:

我不知道有任何预先构建的功能,所以这里有一个函数:

def xor[T](x: Option[T], y: Option[T]): Option[T] = (x, y) match {
    case (Some(_), None) => x
    case (None, Some(_)) => y
    case (None, None) => None
    case _ => throw new Exception()
}

【讨论】:

  • 内置函数叫做“orElse”,唯一不同的是输入Some(2) orElse Some(8)会返回Some(2)
【解决方案2】:

这是我的函数版本

val options = Seq(one, two).flatten
if (options.size > 1) throw new Exception("two options were provided")
options.headOption

【讨论】:

  • 似乎不必要地晦涩难懂。检查两种明显的方式(直接翻译你的规范!(one.ifDefined && two.isDefined)然后one orElse b
  • 最近有一个关于检查许多选项的形式的问题,它可以扩展。
  • @som-snytt 有链接吗?
【解决方案3】:
def xor[T](a: Option[T], b: Option[T]) = 
    a orElse b ensuring (_ => a zip b isEmpty)

【讨论】:

  • 我喜欢它,但改进了:(one orElse two) ensuring (_ => (one, two).zipped.isEmpty)
  • 起初我认为 zipped 可能会有所帮助,但它只是使 Lists 和 isEmpty 使用 Tuple2Zipped.foreach 来迭代一个元素。
  • 是的...同样的旧 if-else 更快,但类似英语的代码更具可读性,我认为两者都很好
【解决方案4】:

对于简单的情况,我可能会选择老式的 if-else。

scala> implicit class optxor[A](val opt: Option[A]) extends AnyVal {
     | def xor(other: Option[A]) = if (opt.isEmpty) other else if (other.isEmpty) opt else ??? }
defined class optxor

scala> one xor two
res18: Option[Int] = Some(2)

scala> two xor three
scala.NotImplementedError: an implementation is missing
  at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225)
  at optxor$.xor$extension(<console>:8)
  ... 33 elided

【讨论】:

  • +1。有时候if ... then ... else ...没有错
【解决方案5】:

我会使用 gzou 回答的模式匹配函数来解决这个问题,但这里有一个单行:

one.map{x => two.foreach(y => throw new Exception("both defined")); x}.orElse(two)

【讨论】:

    【解决方案6】:

    我们可以将 2 个Options 与Option++ 运算符组合为Iterable,从而在可迭代对象而不是选项元组上进行模式匹配:

    optionA ++ optionB match {
      case Seq(x, y) => throw new Exception
      case x => x.headOption
    }
    // None and None       => None
    // Some(5) and None    => Some(5)
    // None and Some(5)    => Some(5)
    // Some(5) and Some(3) => Exception
    

    注意headOption,它可以很好地处理包含 1 个元素的列表和空列表。

    【讨论】:

      【解决方案7】:

      或者,

      scala> val one: Option[Int] = None
      one: Option[Int] = None
      
      scala> val two = Option(2)
      two: Option[Int] = Some(2)
      
      scala> val three = Option(3)
      three: Option[Int] = Some(3)
      
      scala> (for (_ <- one; _ <- two) yield ???) orElse one orElse two
      res0: Option[Int] = Some(2)
      
      scala> (for (_ <- three; _ <- two) yield ???) orElse three orElse two
      scala.NotImplementedError: an implementation is missing
        at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225)
        at $anonfun$1$$anonfun$apply$1.apply(<console>:10)
        at $anonfun$1$$anonfun$apply$1.apply(<console>:10)
        at scala.Option.map(Option.scala:145)
        at $anonfun$1.apply(<console>:10)
        at $anonfun$1.apply(<console>:10)
        at scala.Option.flatMap(Option.scala:170)
        ... 33 elided
      

      去糖

      scala> one flatMap (_ => two) map (_ => ???) orElse one orElse two
      res3: Option[Int] = Some(2)
      

      但从习惯上来说,很难想象其中任何一个都可以做到。

      【讨论】:

        【解决方案8】:

        这个怎么样?

        import scala.util.Try
        
        object XorOption {
        
          def xorOptions[T](one: Option[T], two: Option[T]): Try[Option[T]] = {
            Try {
              for (x <- one; y <- two) throw new RuntimeException
              one.orElse(two)
            }
          }
        
          def main(args: Array[String]) {
            val testData = List(
              (None, None),
              (None, Some(2)),
              (Some(1), None),
              (Some(1), Some(2)))
        
            for (t <- testData) {
              println(t + " => " + xorOptions(t._1, t._2))
            }
        
          }
        
        }
        

        输出:

        (None,None) => Success(None)
        (None,Some(2)) => Success(Some(2))
        (Some(1),None) => Success(Some(1))
        (Some(1),Some(2)) => Failure(java.lang.RuntimeException)
        

        【讨论】:

          【解决方案9】:
          List(Some(1), Some(2)).flatten match {
             case x :: Nil => Some(x)
             case Nil => None
             case _ => throw new Exception
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-10-31
            • 1970-01-01
            • 1970-01-01
            • 2022-01-07
            • 2020-07-28
            • 2020-12-04
            相关资源
            最近更新 更多