7 Stimmen

Binärer Operator mit Option-Argumenten

In Scala, wie definiere ich die Addition über zwei Option-Argumente? Um genau zu sein, sagen wir, sie sind Wrapper für Int-Typen (ich arbeite eigentlich mit Maps von Doubles, aber dieses Beispiel ist einfacher).

Ich habe folgendes versucht, aber es gibt mir nur einen Fehler:

  def addOpt(a:Option[Int], b:Option[Int]) = {
    a match {
      case Some(x) => x.get
      case None => 0
    } + b match {
      case Some(y) => y.get
      case None => 0
    }
  }

Bearbeitet, um hinzuzufügen:

In meinem tatsächlichen Problem füge ich zwei Maps zusammen, die Platzhalter für Sparse-Vektoren sind. Also gibt der None-Fall Map[Int, Double] zurück und das + ist eigentlich ein ++ (mit der Anpassung auf stackoverflow.com/a/7080321/614684)

2 Stimmen

Du extrahierst den Inhalt der Option nicht auf die richtige Weise. Wenn du Fallentscheidungen wie Some(x) hast, ist x der Wert innerhalb der Option (Typ Int) und du rufst get nicht darauf auf. Fallentscheidungen wie Some(x) => x. Wie auch immer, wenn du den Inhalt oder den Standardwert haben möchtest, ist a.getOrElse(0) bequemer.

0 Stimmen

@didierd DANKE! Das war die Antwort, die ich brauchte. Kannst du es in eine Antwort umwandeln? Ich werde deine wählen.

0 Stimmen

Verpflichtender Verweis: blog.tmorris.net/scalaoption-cheat-sheet

26voto

oxbow_lakes Punkte 131223

Monoiden

Sie werden feststellen, dass das Leben viel einfacher wird, wenn Sie erkennen, dass Sie auf den Schultern von Riesen stehen und von gemeinsamen Abstraktionen und den Bibliotheken profitieren können, die für ihre Verwendung erstellt wurden. Zu diesem Zweck geht es bei dieser Frage im Grunde darum, mit Monoiden umzugehen (siehe unten stehende verwandte Fragen für weitere Informationen dazu) und die genannte Bibliothek heißt scalaz.

Mit scalaz FP ist dies einfach:

def add(a: Option[Int], b: Option[Int]) = ~(a |+| b)

Darüber hinaus funktioniert dies für jeden Monoid M:

def add[M: Monoid](a: Option[M], b: Option[M]) = ~(a |+| b)

Noch nützlicher ist, dass es für beliebig viele von ihnen innerhalb eines Foldable-Containers funktioniert:

def add[M: Monoid, F: Foldable](as: F[Option[M]]) = ~as.asMA.sum

Beachten Sie, dass einige recht nützliche Monoiden, abgesehen von den offensichtlichen Int, String, Boolean

  1. Map[A, B: Monoid]
  2. A => (B: Monoid)
  3. Option[A: Monoid]

In der Tat lohnt es sich kaum, Ihre eigene Methode zu extrahieren:

scala> some(some(some(1))) #:: some(some(some(2))) #:: Stream.empty
res0: scala.collection.immutable.Stream[Option[Option[Option[Int]]]] = Stream(Some(Some(Some(1))), ?)

scala> ~res0.asMA.sum
res1: Option[Option[Int]] = Some(Some(3))

Einige verwandte Fragen

Q. Was ist ein Monoid?

Ein Monoid ist ein Typ M, für den es eine assoziative binäre Operation (M, M) => M und ein Identitätselement I unter dieser Operation gibt, sodass mplus(m, I) == m == mplus(I, m) für alle m vom Typ M

Q. Was ist |+|?

Dies ist nur eine Abkürzung in scalaz (oder ASCII-Wahnsinn, je nachdem) für die mplus binäre Operation

Q. Was ist ~?

Es ist ein unärer Operator, der "oder Identität" bedeutet und (unter Verwendung impliziter Konvertierungen von Scala) von der scalaz-Bibliothek für Option[M] hinzugefügt wird, wenn M ein Monoid ist. Offensichtlich gibt eine nicht leere Option ihren Inhalt zurück; eine leere Option wird durch das Identitätselement des Monoids ersetzt.

Q. Was ist asMA.sum?

Ein Foldable ist im Grunde eine Datenstruktur, über die gefaltet werden kann (wie z.B. foldLeft). Beachten Sie, dass foldLeft einen Startwert und eine Operation zur Aneinanderreihung aufeinanderfolgender Berechnungen benötigt. Im Fall des Summierens eines Monoids ist der Startwert das Identitätselement I und die Operation ist mplus. Sie können also asMA.sum auf einem Foldable[M: Monoid] aufrufen. Möglicherweise müssen Sie asMA verwenden, um einen Namenskonflikt mit der sum-Methode der Standardbibliothek zu vermeiden.

Einige Referenzen

  • Folien und Video eines Vortrags, den ich gehalten habe, der praktische Beispiele für die Verwendung von Monoiden in der Praxis liefert

10voto

Luigi Plinge Punkte 49666
def addOpts(xs: Option[Int]*) = xs.flatten.sum

Dies funktioniert für beliebige Anzahlen von Eingaben.

1 Stimmen

FWIW, um dies für Map[Int, Double] zu ermöglichen, lautet der Code einfach: def addOpts(xs: Option[Map[Int, Double]]*) = xs.flatten.foldLeft(Map.empty[Int, Double]) { _ ++ _ }

1 Stimmen

Tatsächlich. Oder die scalaz-Version, die mit beiden funktioniert: def addOpts[T:Monoid](xs: Option[T]*) = xs.flatten.asMA.sum

5voto

Maurício Linhares Punkte 38957

Wenn sie beide standardmäßig auf 0 gesetzt sind, benötigen Sie kein Musterabgleich:

  def addOpt(a:Option[Int], b:Option[Int]) = {
    a.getOrElse(0) + b.getOrElse(0)
  }

0 Stimmen

In meinem konkreten Problem gibt es keine. Ich füge zwei Karten hinzu, die als Platzhalter für dünn besetzte Vektoren dienen.

0 Stimmen

Wenn Sie keinen Standardwert für beide haben, können Sie in diesem Fall Option nicht verwenden, da einer oder beide von ihnen möglicherweise nicht definiert sind.

0 Stimmen

Was ist, wenn ich sie standardmäßig auf Map[Int, Double] (was sie tatsächlich sind) ändere und das + tatsächlich ein ++ ist (mit der Änderung unter stackoverflow.com/a/7080321/614684)?

4voto

Didier Dupont Punkte 29128

(Wie oben bereits kommentiert, in einer Antwort wiederholt)

Sie extrahieren den Inhalt der Option nicht richtig. Wenn Sie mit case Some(x) übereinstimmen, ist x der Wert innerhalb der Option (Typ Int) und Sie rufen nicht get darauf auf. Mach einfach

case Some(x) => x 

Wie auch immer, wenn Sie den Inhalt oder den Standardwert möchten, ist a.getOrElse(0) praktischer

0voto

Knut Arne Vedaa Punkte 14822
def addOpt(ao: Option[Int], bo: Option[Int]) =
    for {
        a <- ao
        b <- bo
    } yield a + b

0 Stimmen

Dies führt Option[Int] zurück, anstelle von Int. Außerdem gibt es None zurück, wenn entweder ao oder bo None ist, was nicht das gewünschte Ergebnis ist.

0 Stimmen

Du hast Recht, dass es nicht das beabsichtigte Ergebnis des Codes des Fragestellers erzeugt, aber ich würde sagen, es behandelt den Geist des Titels der Frage.

0 Stimmen

Während es nicht 0 zurückgibt wie die Originalantwort, wird einfach das Hinzufügen von getOrElse den Trick tun

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X