Scala: карта с двумя или более опциями - PullRequest
11 голосов
/ 29 апреля 2011

По сути, я ищу самый похожий на скалы способ сделать следующее:

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 или использовать какой-то цикл.

EDIT-1:

При написании вопроса я придумал что-то вроде ответа:

def sum2(value1: Option[Int], value2: Option[Int]): Option[Int] = 
  value1.toList ::: value2.toList reduceLeftOption { _ + _ }

Этот выглядит очень идиоматичным для моего неопытного глаза. Это будет работать даже с более чем двумя значениями. Но можно ли сделать то же самое без преобразования в списки?

EDIT-2:

Я получил это решение (спасибо ziggystar ):

def sum(values: Option[Int]*): Option[Int] = 
  values.flatten reduceLeftOption { _ + _ }

EDIT-3:

Еще одна альтернатива благодаря Landei :

def sum(values: Option[Int]*): Option[Int] = 
  values collect { case Some(n) => n } reduceLeftOption { _ + _ }

Ответы [ 5 ]

10 голосов
/ 29 апреля 2011

Как насчет:

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.

7 голосов
/ 29 апреля 2011
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
5 голосов
/ 30 апреля 2011

Другое решение:

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
4 голосов
/ 17 апреля 2016

Вы можете сделать это очень кратким, используя тот факт, что для Option есть экземпляр Semigroup, который делает именно то, что вы хотите. Вы можете использовать скалаз или кошек . Вот пример использования 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
3 голосов
/ 30 апреля 2011

Сокращенное решение 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
...