Как упростить этот код, который использует Option.fold в Scala?

#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")