Как объединить значения параметров в Scala? - PullRequest
28 голосов
/ 26 апреля 2010

Я хочу иметь возможность применить операцию f: (T,T) => T к Option[T] значениям в Scala. Я хочу, чтобы результат был None, если любое из двух значений None.

Более конкретно, я хочу знать, есть ли более короткий способ сделать следующее:

def opt_apply[T](f: (T,T) => T, x: Option[T], y: Option[T]): Option[T] = {
  (x,y) match {
    case (Some(u),Some(v)) => Some(f(u,v))
    case _ => None
  }
}

Я попытался (x zip y) map {case (u,v) => f(u,v)}, но в результате Iterator[T] не Option[T].

Ответы [ 6 ]

31 голосов
/ 26 апреля 2010
scala> val (x, y) = (Some(4), Some(9))
x: Some[Int] = Some(4)
y: Some[Int] = Some(9)

scala> def f(x: Int, y: Int) = Math.max(x, y)
f: (x: Int,y: Int)Int

scala> for { x0 <- x; y0 <- y } yield f(x0, y0)
res26: Option[Int] = Some(9)

scala> val x = None
x: None.type = None

scala> for { x0 <- x; y0 <- y } yield f(x0, y0)
res27: Option[Int] = None
18 голосов
/ 26 апреля 2010
Ответ

@ RahulG основан на том факте, что Option - это монада (хотя в библиотеке Scala нет типа, который бы представлял это). Компилятор расширяет понимание for до следующего:

def a: Option[Int]
def b: Option[Int]
val calc: Option[Int] = a flatMap {aa => b map {bb => aa + bb}}

Вы также можете рассматривать его как аппликативный функтор, с некоторой помощью Скалаза:

import scalaz._
import Scalaz._

def a: Option[Int]
def b: Option[Int]
val calc: Option[Int] = (a ⊛ b) {_ + _}

Ключевым отличием является то, что при монадическом вычислении сбой (то есть None) вычисления a замыкает оценку. В аппликативном стиле оцениваются и a, и b, и, если оба значения Some s, вызывается чистая функция. Вы также можете видеть, что в монадическом вычислении значение aa могло быть использовано в вычислении b; в аппликативной версии b не может зависеть от результата a.

3 голосов
/ 26 апреля 2010

У меня есть немного более старая версия scalaz , чем retronym , но приведенный ниже пример работает для меня и обобщается для случая, когда у вас есть 3 типа T, U, V, а не только один:

def main(args: Array[String]) {
  import scalaz._
  import Scalaz._

  val opt1 = some(4.0) //Option[Double]
  val opt2 = some(3)   //Option[Int]

  val f: (Double, Int) => String = (d, i) => "[%d and %.2f]".format(i, d)

  val res = (opt1 <|*|> opt2).map(f.tupled)
  println(res) //Some([3 and 4.00])
}

Затем я могу добавить:

val opt3 = none[Int]
val res2 = (opt1 <|*|> opt3).map(f.tupled)
println(res2) //None
1 голос
/ 26 апреля 2010

Вы можете использовать для понимания:

def opt_apply[T](f: (T,T) => T, x: Option[T], y: Option[T]): Option[T] = 
     for (xp <- x; yp <- y) yield (f(xp,yp))

Что такое сахар для:

x flatMap {xp => y map {yp => f(xp, yp)}}

Это также возможно благодаря тому, что Option является монадой

0 голосов
/ 09 февраля 2019

Начиная с Scala 2.13, Option#zip можно применить к другому Option для возврата Option (в более ранних версиях он возвращал Iterable); таким образом:

def optApply[T](f: (T,T) => T, a: Option[T], b: Option[T]): Option[T] =
  a.zip(b).map(f.tupled)

где поведение zip:

Some(2).zip(Some(3)) // Some((2, 3))
Some(2).zip(None)    // None
None.zip(Some(3))    // None
None.zip(None)       // None

и которые могут применяться как таковые:

optApply[Int]((a, b) => a max b, Some(2), Some(5)) // Some(5)
optApply[Int]((a, b) => a max b, Some(2), None)    // None
0 голосов
/ 08 ноября 2017
def optApply[A,B,C](f: (A, B) => C, a: Option[A], b: Option[B]): Option[C] =
  a.zip(b).headOption.map { tup => f.tupled(tup) }

a.zip(b) приводит к Итерируемому [(A, B)] (с, потому что это из Опций, самое большее с одним элементом). headOption затем возвращает первый элемент как Option.

...