Обработка двух опций в Scala - PullRequest
2 голосов
/ 12 марта 2020

У меня есть 2 варианта, и мне нужно взять среднее из значений, которые они содержат.

Возможно, что один или оба могут отсутствовать. Если одно из значений отсутствует, я бы просто взял другое в качестве среднего. Но если оба значения отсутствуют, я бы прибегнул к некоторому значению по умолчанию.

Как это можно сделать чистым способом?

Я могу проверить отсутствие значения с помощью isEmpty, но тогда не буду это было бы так же, как нулевая проверка?

Ответы [ 5 ]

6 голосов
/ 12 марта 2020

Полагаю, это говорит само за себя:

val option1 = Some(12.0)
val option2 = None
val default = 0.0

val average = (option1, option2) match {
   case (Some(val1), Some(val2)) => (val1 + val2) / 2
   case (None, Some(val2)) => val2
   case (Some(val1), None) => val1
   case (None, None) => default
}

... но если нет, то основная идея c заключается в том, что вы создаете набор опций, а затем сопоставляете шаблон с кортежем.

Преимущество этого заключается в явном захвате всех четырех потенциальных случаев + при поддержке со стороны компилятора - поскольку Option является запечатанной чертой, компилятор может проверить и убедиться, что все потенциальные ветви сопоставления с образцом покрыты.

4 голосов
/ 12 марта 2020

Вы можете рассматривать Опции как Seq:

val o: Option[Double]
val p: Option[Double]
val default: Double

val s = o.toSeq ++ p.toSeq

val avg = s.reduceOption(_ + _).getOrElse(default) / 1.max(s.size)
1 голос
/ 12 марта 2020
val v = List(opt1, opt2).flatten

if (v.nonEmpty) {
  v.sum / v.size
} else {
  <default value>
}

Это может быть расширено для работы с любым количеством дополнительных значений.

0 голосов
/ 12 марта 2020

По моему мнению, вы должны сохранить среднее значение в параметре и впоследствии получить значение по умолчанию.

def avgOpt(of:Option[Double]*) = {
  val s = of.flatten
  s.reduceOption(_ + _).map(_ / s.size)
}

avgOpt(Some(5), None, None).getOrElse(0)    //5
avgOpt(Some(5), Some(3), None).getOrElse(0) //4
avgOpt(None, None).getOrElse(0)             //0
0 голосов
/ 12 марта 2020

Другая возможность:

def avg(left: Option[Double], right: Option[Double])(default: => Double): Double =
  left.flatMap(a => right.map(b => (a + b) / 2))
    .orElse(left)
    .orElse(right)
    .getOrElse(default)

Вы flatMap над левой опцией: если она не пустая, вы выбираете правильную опцию и map ее содержимое и усредняете с содержанием левой опции. Если какой-либо из параметров пуст, результат будет None, поэтому вы можете определить либо left, либо right как запасные значения с помощью orElse. Наконец, результат извлекается с помощью getOrElse, и, если оба входа пустые, возвращается default.

Вы можете адаптировать его для принятия любого поведения. Чтобы создать функцию, которая выдает, если обе опции пусты, вы можете сделать следующее:

val assertAvg = avg(_ : Option[Double], _ : Option[Double])(sys.error("both operands are empty"))

Это работает, потому что типом throw выражений является Nothing, который является подтипом любого другого типа ( включая Double), т.е. он может быть возвращен в результате любого выражения, независимо от ожидаемого типа.

Код (и некоторые тесты) доступны здесь, в Scast ie .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...