for..else для типов Option в Scala? - PullRequest
17 голосов
/ 31 июля 2011

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

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

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

Ответы [ 9 ]

26 голосов
/ 31 июля 2011

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

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

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

13 голосов
/ 31 июля 2011

Вы можете сопоставить образец с Options одновременно:

(xMaybe, yMaybe) match {
  case (Some(x), Some(y)) => "x and y are there"
  case _ => "x and/or y were None"
}
7 голосов
/ 01 августа 2011

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

  1. T[F[A]]
  2. 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 => Option [Y] =...

и вызов traverse

  • val r = z traverse f

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

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

5 голосов
/ 31 июля 2011

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

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
}
4 голосов
/ 01 августа 2011

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

((xMaybe |@| yMaybe) { (x, y) => /* do something */ }).getOrElse(/* something else */)
3 голосов
/ 31 июля 2011

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

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

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

РЕДАКТИРОВАТЬ: Только что увидел, что Эмиль Иванов решение немного элегантнее.

0 голосов
/ 08 июня 2019

Начиная с Scala 2.13, мы можем альтернативно использовать Option#zip, который объединяет два параметра в некоторый набор их значений, если оба параметра определены или нет:

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

Илис Option#fold:

(opt1 zip opt2).fold("x and/or y were None"){ case (x, y) => "x and y are there" }
0 голосов
/ 01 августа 2011

Я думаю, что ключевой момент здесь - думать о типах как о том, что вы хотите сделать.Насколько я понимаю, вы хотите перебрать список пар Option, а затем сделать что-то на основе определенного условия.Таким образом, интересным будет ваш вопрос: как будет выглядеть возвращаемый тип, кроме вас?Я думаю, что это будет выглядеть примерно так: либо [Список [Опция], Список [Опция, Опция]].на стороне ошибки (слева) вы накапливаете опцию, которая была в паре с None (и, так сказать, осталась одна).На правой стороне вы суммируете непустые параметры, которые представляют ваши успешные значения.Так что нам просто нужна функция, которая делает именно это.Проверяйте каждую пару и накапливайте ее в соответствии с ее результатом (успех - неудача).Я надеюсь, что это поможет, если нет, пожалуйста, объясните более подробно ваш вариант использования.Некоторые ссылки для реализации того, что я описал: 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/

0 голосов
/ 31 июля 2011

Для масштабирования до множества опций попробуйте что-нибудь из следующего:

 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>
...