Объедините несколько функций в Scala, пока выполняется условие - PullRequest
4 голосов
/ 11 мая 2019

Предположим, у нас есть 3 метода, которые все возвращают Option

import scala.util.Try

def divideSafe(d: Int): Option[Int] = Try(42 / d).toOption
def sqrtSafe(x: Double): Option[Double] = if(!math.sqrt(x).isNaN) Some(math.sqrt(x)) else None
def convertSafe(s: String): Option[Int] = Try(s.toInt).toOption

Теперь я хочу связать их, пока выполняется условие. В моем случае, если результат предыдущего метода определен, то следует перейти к следующему. И пока условие ложно, остановите операцию и верните значение по умолчанию.

Я могу получить желаемый результат с помощью вложенных операторов if-else:

def superSafe(d: Int, x: Double, s: String): Unit = {
  if (divideSafe(d).isDefined) {
    println(divideSafe(d).get.toString)
    if (sqrtSafe(x).isDefined) {
      println(sqrtSafe(x).toString)
      if (convertSafe(s).isDefined) {
        println(convertSafe(s).get.toString)
      } else {
        println("Converting failed")
      }
    } else {
      println("Sqrt failed")
    }
  } else {
    println("Dividing failed")
  }
}

Таким образом:

superSafe(1, -1, "5") напечатает 42, и Sqrt потерпел неудачу superSafe(0, 4, "cat") будет печатать Разделение не удалось superSafe(42, 4, 1) будет печатать 1,2,1

Однако я хочу избежать вложенных операторов if-else и мне интересно, есть ли в Scala функциональный способ решения подобных проблем.

Я хочу что-то вроде orElse заявления, но наоборот.

Желаемую функцию можно использовать так:

divideSafe(42) ifThen("Dividing failed") sqrtSafe(-4) ifThen("Sqrt failed") convertSafe("cat") ifThen("Converting failed")

Ответы [ 2 ]

6 голосов
/ 11 мая 2019

Вы используете Option, который не подходит для вашего варианта использования. Я полагаю, что Either будет работать лучше в вашем примере, потому что он может содержать информацию о неудаче (Left) или об успехе (Right). Если вы переписали свои функции для использования Either, это может выглядеть так:

def divideSafe(d: Int): Either[String, Int] = Try(42 / d).toOption.toRight("Divide failed")
def sqrtSafe(x: Double): Either[String, Double] = math.sqrt(x) match {
   case s if s.isNaN => Left("Sqrt failed") //you can use pattern matching to calculate square just once
   case s => Right(s)
} 
def convertSafe(s: String): Either[String, Int] = Try(s.toInt).toOption.toRight("Converting failed")

И тогда вы можете использовать for comprehension в вашем superSafe методе:

def superSafe(d: Int, x: Double, s: String): Unit = {
  val r: Either[String, (Int, Double, Int)] = for { //for-comprehension shortcircuits
    r1 <- divideSafe(d) //if divideSafe fails it whole comprehension will return Left(Divide failed)
    r2 <- sqrtSafe(x) //if it succeds it will go further
    r3 <- convertSafe(s)
  } yield (r1,r2,r3)

  r match { 
    case Right((r1,r2,r3)) => { //you could move it inside yield, I splitted it for clarity
      println(r1)
      println(r2)
      println(r3)
    }
    case Left(e) => println(e)
  }
}
0 голосов
/ 12 мая 2019

Если вам нравится более функциональный, вы можете попробовать это:

import scala.util.Try

def divideSafe(d: Int): Option[Int] = Try(42 / d).toOption
def sqrtSafe(x: Double): Option[Double] = if(!math.sqrt(x).isNaN) Some(math.sqrt(x)) else None
def convertSafe(s: String): Option[Int] = Try(s.toInt).toOption

def superSafe(d: Int, x: Double, s: String): Any = {
  divideSafe(d)
    .map(rd => sqrtSafe(x)
    .map(rx => convertSafe(s)
    .map(rs => (rd, rx, rs))
    .getOrElse((rd, rx, "Converting failed")))
    .getOrElse((rd, "Sqrt failed")))
    .getOrElse("Dividing failed")
}

println(superSafe(1, 1, "1"))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...