Скала: Карта с двумя или более параметров
-
27-10-2019 - |
Вопрос
По сути, я ищу самый похожий на скала способ сделать следующее:
def sum(value1: Option[Int], value2: Option[Int]): Option[Int] =
if(value1.isDefined && value2.isDefined) Some(value1.get + value2.get)
else if(value1.isDefined && value2.isEmpty) value1
else if(value1.isEmpty && 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
s или использовать какой -то цикл.
РЕДАКТИРОВАТЬ-1:
Во время написания вопроса я придумал своего рода ответ:
def sum2(value1: Option[Int], value2: Option[Int]): Option[Int] =
value1.toList ::: value2.toList reduceLeftOption { _ + _ }
Это выглядит очень идиоматическим для моего неопытного глаза. Это даже сработало бы с более чем двумя ценностями. Тем не менее, возможно ли сделать то же самое, не преобразуя списки?
Редактировать-2:
Я закончил этим решением (благодаря Зиггистар):
def sum(values: Option[Int]*): Option[Int] =
values.flatten reduceLeftOption { _ + _ }
Редактировать-3:
Еще одна альтернатива благодаря Ландей:
def sum(values: Option[Int]*): Option[Int] =
values collect { case Some(n) => n } reduceLeftOption { _ + _ }
Решение
Как насчет:
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, если бы все аргументы не были. В этом случае функция уменьшится до values.flatten.sum
.
Другие советы
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
Другое решение:
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
Вы можете сделать это очень кратким, используя тот факт, что есть Semigroup
экземпляр для Option
Это делает именно то, что вы хотите. Вы можете использовать Скалаз или же кошки. Анкет Вот пример с использованием 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
Уменьшенное решение Michael.kebe с небольшим взглядом на некоторые основные математические правила:
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