Для... для типов опций в Scala?

Предположим, у меня есть два параметра и, если они оба являются Some, выполните один путь кода, и если примечание, выполните другое. Я хотел бы сделать что-то вроде

for (x <- xMaybe; y <- yMaybe) {
  // do something
}
else {
  // either x or y were None, handle this
}

Вне операторов if или сопоставления шаблонов (которые могут не масштабироваться, если у меня было более двух параметров), есть ли лучший способ справиться с этим?

Ответы

Ответ 1

Очень близко к вашему предложению синтаксиса, используя yield, чтобы обернуть вывод for в опции:

val result = { 
  for (x <- xMaybe; y <- yMaybe) yield {
    // do something
  }
} getOrElse {
  // either x or y were None, handle this
}

Блок getOrElse выполняется только в том случае, если один или оба параметра имеют значение None.

Ответ 2

Вы можете одновременно сопоставлять как Options:

(xMaybe, yMaybe) match {
  case (Some(x), Some(y)) => "x and y are there"
  case _ => "x and/or y were None"
}

Ответ 3

Функция traverse в Scalaz обобщает вашу проблему здесь. Он принимает два аргумента:

  • T[F[A]]
  • A => F[B]

и возвращает F[T[B]]. T - любая сквозная структура данных, такая как List, а F - любой прикладной функтор, такой как Option. Поэтому для специализации ваша желаемая функция имеет такой тип:

  • List[Option[A]] => (A => Option[B]) => Option[List[B]]

Итак, поместите все ваши значения Option в List

  • val z = List(xMaybe, yMaybe)

Создайте полученную функцию, однако вы хотите получить результаты:

  • val f: X = > Опция [Y] =...

и вызовите traverse

  • val r = z траверс f

Эти шаблоны программирования возникают очень часто. В нем есть статья, в которой говорится об этом, Суть шаблона итератора.

note: Я просто хотел исправить URL, но CLEVER edit help сообщает мне, что мне нужно изменить не менее 6 символов, поэтому я также включил эту полезную ссылку (примеры scala):
http://etorreborre.blogspot.com/2011/06/essence-of-iterator-pattern.html

Ответ 4

Почему что-то подобное не работает?

val opts = List[Option[Int]](Some(1), None, Some(2))
if (opts contains None) {
  // Has a None
} else {
  // Launch the missiles
  val values = opts.map(_.get) // We know that there is no None in the list so get will not throw
}

Ответ 5

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

((xMaybe |@| yMaybe) { (x, y) => /* do something */ }).getOrElse(/* something else */)

Ответ 6

Вы сказали, что хотите, чтобы решение было масштабируемым:

val optional = List(Some(4), Some(3), None)

if(optional forall {_.isDefined}) {
    //All defined
} else {
    //At least one not defined
}

EDIT: Просто увидел, что решение Эмиля Иванова немного более элегантно.

Ответ 7

Для масштабирования многих параметров попробуйте что-то в этом направлении:

 def runIfAllSome[A](func:(A)=>Unit, opts:Option[A]*) = {
   if(opts.find((o)=>o==None) == None) for(opt<-opts) func(opt.get)
 }

С помощью этого вы можете сделать:

scala> def fun(i:Int) = println(i)
fun: (i: Int)Unit

scala> runIfAllSome(fun, Some(1), Some(2))
1
2

scala> runIfAllSome(fun, None, Some(1))

scala>

Ответ 8

Я думаю, что ключевым моментом здесь является думать о типах как о том, что вы хотите делать. Насколько я понимаю, вы хотите перебрать список пар Option и затем сделать что-то, основанное на определенном условии. Таким образом, интересный бит вашего вопроса будет таким: как бы тип возвращаемого типа выглядел бы так, как если бы вы были? Я думаю, что это будет выглядеть примерно так: Либо [Список [Вариант], Список [Опция, Опция]]. на стороне ошибки (слева) вы накапливали опцию, которая была сопряжена с None (и осталась одна, так сказать). С правой стороны вы суммируете непустые опции, которые представляют ваши успешные значения. Поэтому нам просто нужна функция, которая делает именно это. Проверяйте каждую пару и накапливайте ее в соответствии с результатом (успех - сбой). Надеюсь, это поможет, если не объясните, пожалуйста, более подробно ваш usecase. Некоторые ссылки для реализации того, что я описал: http://applicative-errors-scala.googlecode.com/svn/artifacts/0.6/pdf/index.pdf и: http://blog.tmorris.net/automated-validation-with-applicatives-and-semigroups-for-sanjiv/

Ответ 9

Начиная с Scala 2.13, мы можем альтернативно использовать :A,B](that:Option[B]):Option[(A1,B)] rel="nofollow noreferrer"> Option#zip который объединяет два параметра в некоторый кортеж их значений, если определены оба параметра или нет:

opt1 zip opt2 match {
  case Some((x, y)) => "x and y are there"
  case None         => "x and/or y were None"
}

Или с B)(f:A=>B):B rel="nofollow noreferrer"> Option#fold:

(opt1 zip opt2).fold("x and/or y were None"){ case (x, y) => "x and y are there" }