Для... для типов опций в 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 обобщает вашу проблему здесь. Он принимает два аргумента:
и возвращает 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
Эти шаблоны программирования возникают очень часто. В нем есть статья, в которой говорится об этом, Суть шаблона итератора.
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" }