Scala: Karte mit zwei oder mehr Optionen
-
27-10-2019 - |
Frage
Grundsätzlich suche ich nach der skalalischsten Möglichkeit, Folgendes zu tun:
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
Dies gibt die richtige Ausgabe:
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
Doch um mehr als zwei Optionen zusammenzufassen, würde ich viel zu viele verwenden if
s oder verwenden Sie eine Art Schleife.
Edit-1:
Während ich die Frage schrieb, stellte ich eine Art Antwort auf:
def sum2(value1: Option[Int], value2: Option[Int]): Option[Int] =
value1.toList ::: value2.toList reduceLeftOption { _ + _ }
Dieser sieht für mein unerfahrenes Auge sehr idiomatisch aus. Dies würde sogar mit mehr als zwei Werten funktionieren. Ist jedoch möglich, dasselbe zu tun, ohne in Listen zu konvertieren?
Edit-2:
Am Ende hatte ich diese Lösung (danke an ZigyStar):
def sum(values: Option[Int]*): Option[Int] =
values.flatten reduceLeftOption { _ + _ }
Edit-3:
Ein weiterer Alternative Dank an Landei:
def sum(values: Option[Int]*): Option[Int] =
values collect { case Some(n) => n } reduceLeftOption { _ + _ }
Lösung
Wie wäre es mit:
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
Bearbeiten
Vielleicht wäre es gesund, 0 zurückzukehren, wenn alle Argumente keine wären. In diesem Fall würde sich die Funktion aufnehmen auf values.flatten.sum
.
Andere Tipps
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
Eine andere Lösung ist:
def sum(values: Option[Int]*): Int = values.collect{case Some(n) => n}.sum
Während im aktuellen Fall flatten
ist eindeutig bequemer, die collect
Die Version ist flexibler, da sie Zuordnungen ausführen und zusätzliche Filterbedingungen oder komplexe Muster aufweisen können. ZB stellen Sie sich vor, Sie möchten die Summe der Quadrate aller gleichmäßigen Zahlen in Werten haben:
values.collect{case Some(n) if n mod 2 == 0 => n*n}.sum
Sie können es mit der Tatsache, dass es eine gibt, sehr präzise machen Semigroup
Instanz für Option
Das tut genau das, was Sie wollen. Sie können verwenden Scalaz oder Katzen. Hier ist ein Beispiel verwendet 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)
Also dein sum
wird:
def sum(v1: Option[Int], v2: Option[Int]): Option[Int] = v1 |+| v2
Reduzierte Lösung von Michael.kebe mit ein wenig Blick auf einige grundlegende mathematische Regeln:
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