#scala #option #fold
#scala #свернуть #scala-option
Вопрос:
Предположим, у меня есть следующие функции
def foo(x: Int): Option[String] = ...
def bar(s: String): Option[String] = ...
и
def f(s: String): String = ...
def g(s: String): String = ...
Я хотел бы составить все эти функции следующим образом:
def qux(x: Int): String = {
val s1 = foo(x).fold("defaultFoo") { str => f(str) }
bar(s1).fold("defaultBar") { str => g(str) }
}
На мой взгляд, функция qux
выглядит не особенно элегантно, и мне интересно, как ее упростить. Как бы вы предложили написать qux
?
Комментарии:
1. Это
str = g(str)
должно бытьstr => g(str)
?2. @DonRoby Да, я исправляю код.
3.
x.fold(default)(x => f(x)) == x.fold(default)(f)
делает его приятнее4. @sschaef Спасибо за исправление. Вы правы.
Ответ №1:
С простым использованием map
:
def quz(x: Int) =
bar(foo(x).fold("defaultFoo")(f))
.map(g).getOrElse("defaultBar")
Комментарии:
1. Где здесь «defaultFoo»?
2. @Michael ops, да, тогда не так красиво
3. Я бы рекомендовал выбрать либо
fold
илиmap
/getOrElse
. Похоже, в основном это вопрос вкуса при выборе между ними. Но зачем использовать оба?
Ответ №2:
Чтобы кодифицировать то, что сказали другие, первым серьезным улучшением было бы позволить eta-расширению работать на вас вместо ручного создания замыканий:
def qux(x: Int): String = {
val s1 = foo(x).fold("defaultFoo")(f)
bar(s1).fold("defaultBar")(g)
}
Вы также можете использовать for...yield
понимание, чтобы отложить ваш случай по умолчанию до конца:
def qux(x: Int): String = {
val result = for { s1 <- foo(x); s2 <- bar(f(s1)) } yield { g(s2) }
result.getOrElse("default result")
}
Проблема здесь в том, что вы теряете обработку ошибок, которая у вас есть в середине вашего потока управления, потому что более простые комбинаторы проходят через None
cases . Если вам нужно сохранить тот же поток, вы можете попробовать это:
def qux(x: Int): String = {
val s1 = foo(x).map(f).getOrElse("defaultFoo")
bar(s1).map(g).getOrElse("defaultBar")
}
Это не намного проще, чем ваш исходный код, но он немного более лаконичен и переносит обработку ошибок в конец каждой строки. Или просто оставьте промежуточную переменную и сделайте ее однострочной:
def qux(x: Int): String = bar(foo(x).map(f).getOrElse("defaultFoo")).map(g).getOrElse("defaultBar")