Идиоматическая скала для получения одной опции из двух опций и выдачи исключения, если две доступны
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, но кажется, что он предназначен для "Представления значения одного из двух возможных типов". Я пропускаю какую-то структуру данных или монаду? По сути, я хочу, чтобы явное (и выдача ошибок, если оба являются ценными) получить либо один, если он доступен, либо получить None
8 ответов
Я не знаю ни одной предварительной сборки, чтобы сделать это, поэтому вот функция:
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()
}
def xor[T](a: Option[T], b: Option[T]) =
a orElse b ensuring (_ => a zip b isEmpty)
Вот моя версия функции
val options = Seq(one, two).flatten
if (options.size > 1) throw new Exception("two options were provided")
options.headOption
Я бы, наверное, пошел в старую школу, если бы еще, для простого случая.
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
Мы можем объединить 2 Option
с как Iterable
с Option
"s ++
оператор, и, следовательно, сопоставление с образцом на итерациях вместо набора опций:
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 элементом, так и пустой список.
Я бы подошел к этому, используя функцию сопоставления с образцом, как ответил gzou, но вот один из них:
one.map{x => two.foreach(y => throw new Exception("both defined")); x}.orElse(two)
Как насчет этого?
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)
List(Some(1), Some(2)).flatten match {
case x :: Nil => Some(x)
case Nil => None
case _ => throw new Exception
}
Или же,
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)
но по идиоматическим причинам трудно представить, что можно сделать что-то из этого.