#scala #scala-option
#scala #scala-опция
Вопрос:
В принципе, я ищу наиболее похожий на scala способ сделать следующее:
def sum(value1: Option[Int], value2: Option[Int]): Option[Int] =
if(value1.isDefined amp;amp; value2.isDefined) Some(value1.get value2.get)
else if(value1.isDefined amp;amp; value2.isEmpty) value1
else if(value1.isEmpty amp;amp; value2.isDefined) value2
else None
Это дает правильный вывод:
sum(Some(5), Some(3)) // result = Some(8)
sum(Some(5), None) // result = Some(5)
sum(None, Some(3)) // result = Some(3)
sum(None, None) // result = None
Тем не менее, чтобы суммировать более двух вариантов, мне пришлось бы использовать слишком много if
ов или использовать какой-то цикл.
РЕДАКТИРОВАТЬ-1:
При написании вопроса я придумал своего рода ответ:
def sum2(value1: Option[Int], value2: Option[Int]): Option[Int] =
value1.toList ::: value2.toList reduceLeftOption { _ _ }
На мой неопытный взгляд, это выглядит очень идиоматично. Это будет работать даже с более чем двумя значениями. И все же возможно ли сделать то же самое без преобразования в списки?
РЕДАКТИРОВАТЬ-2:
В итоге я пришел к этому решению (благодаря ziggystar):
def sum(values: Option[Int]*): Option[Int] =
values.flatten reduceLeftOption { _ _ }
РЕДАКТИРОВАТЬ-3:
Еще одна альтернатива благодаря Landei:
def sum(values: Option[Int]*): Option[Int] =
values collect { case Some(n) => n } reduceLeftOption { _ _ }
Ответ №1:
Как насчет:
scala> def sum(values: Option[Int]*): Option[Int] = values.flatten match {
| case Nil => None
| case l => Some(l.sum)
| }
sum: (values: Option[Int]*)Option[Int]
scala> sum(Some(1), None)
res0: Option[Int] = Some(1)
scala> sum(Some(1), Some(4))
res1: Option[Int] = Some(5)
scala> sum(Some(1), Some(4), Some(-5))
res3: Option[Int] = Some(0)
scala> sum(None, None)
res4: Option[Int] = None
Редактировать
Возможно, было бы разумно вернуть 0, если бы все аргументы были None. В этом случае функция сократилась бы до values.flatten.sum
.
Комментарии:
1. Спасибо за
flatten
Я думаю, это то, чего мне не хватало.
Ответ №2:
scala> def sum(a: Option[Int], b: Option[Int]) = (a,b) match {
| case (Some(x), Some(y)) => Some(x y)
| case (Some(x), None) => Some(x)
| case (None, Some(y)) => Some(y)
| case _ => None
| }
sum: (a: Option[Int],b: Option[Int])Option[Int]
scala> sum(Some(5), Some(3))
res0: Option[Int] = Some(8)
scala> sum(Some(5), None)
res1: Option[Int] = Some(5)
scala> sum(None, Some(3))
res2: Option[Int] = Some(3)
scala> sum(None, None)
res3: Option[Int] = None
Комментарии:
1. Спасибо. Это более идиоматично в том смысле, что использует
match
вместоif
. В противном случае это тот же код, что и в моем первом примере, с теми же ограничениями — вам пришлось бы добавить гораздо большеcase
s, чтобы суммировать более 2 значений.
Ответ №3:
Другим решением является:
def sum(values: Option[Int]*): Int = values.collect{case Some(n) => n}.sum
Хотя в текущем случае flatten
это явно удобнее, collect
версия более гибкая, поскольку позволяет выполнять сопоставления и иметь дополнительные условия фильтрации или сложные шаблоны. Например. представьте, что вы хотите получить сумму квадратов всех четных чисел в значениях:
values.collect{case Some(n) if n mod 2 == 0 => n*n}.sum
Комментарии:
1. Компилятор жалуется на возвращаемый тип: найдено
Int
, требуетсяOption[Int]
. Я понимаю вашу точку зрения оcollect
методе вместоflatten
, но мне все еще нужно сделатьreduceLeftOption
, чтобы получить желаемый результат.
Ответ №4:
Вы можете сделать его очень кратким, используя тот факт, что существует Semigroup
экземпляр для Option
, который делает именно то, что вы хотите. Вы можете использовать scalaz или cats. Вот пример использования cats
:
import cats.std.option._
import cats.syntax.semigroup._
import cats.std.int._
Option(1) | | Option(2) // Some(3)
Option(1) | | None // Some(1)
None | | Option(2) // Some(2)
Таким образом, ваш sum
становится:
def sum(v1: Option[Int], v2: Option[Int]): Option[Int] = v1 | | v2
Ответ №5:
Сокращенное решение Майкла.кеб с небольшим обзором некоторых базовых математических правил:
def sum(a: Option[Int], b: Option[Int]) = (a,b) match {
case (None,None) => None
case _ => Some(a.getOrElse(0) b.getOrElse(0))
}
scala> sum(Some(5), Some(3)) // result = Some(8)
res6: Option[Int] = Some(8)
scala> sum(Some(5), None) // result = Some(5)
res7: Option[Int] = Some(5)
scala> sum(None, Some(3)) // result = Some(3)
res8: Option[Int] = Some(3)
scala> sum(None, None) // result = None
res9: Option[Int] = None