在Scala中使用break返回值

时间:2013-11-23 08:10:04

标签: scala functional-programming break

我有以下要求,我正在检查一个值是否大于10,并且基于我将会中断,否则我将返回一个字符串。以下是我的代码:

import scala.util.control.Breaks._

class BreakInScala {
  val breakException = new RuntimeException("Break happened")

  def break = throw breakException

  def callMyFunc(x: Int): String = breakable(myFunc(x))

  def myFunc(x: Int): String = {
    if (x > 10) {
      "I am fine"
    } else {
      break
    }
  }
}

现在发生的事情是我收到错误消息,说明“类型不匹配;找到:需要单位:字符串”原因是:

def breakable(op: => Unit) 

但接下来我将如何编写一个可以返回值并在需要时中断的函数?

2 个答案:

答案 0 :(得分:2)

这是一种略微奇怪的处理方式(抛出异常),另一种方法可能是定义一个“部分函数”(一个只定义它的域的特定子集的函数有点像这样:

scala> val partial = new PartialFunction[Int, String] {
       | def apply(i : Int) = "some string"
       | def isDefinedAt(i : Int) = i < 10
       }

partial: PartialFunction[Int, String] = <function1>

一旦定义了该函数,您就可以通过执行以下操作将其“提升”为Int类型的选项:

scala> val partialLifted = partial.lift
partialOpt: Int => Option[String] = <function1>

然后,如果您使用超出范围的值调用函数,您将获得“无”作为返回值,否则您将获得字符串返回值。这使得将flatMaps / getOrElse逻辑应用于函数变得更加容易,而不必在整个地方抛出异常......

scala> partialLifted(45)
res7: Option[String] = None

scala> partialLifted(10)
res8:  Option[String] = Some(return string)

IMO,这是一种稍微更具功能性的做事方式......希望有所帮助

答案 1 :(得分:2)

Scala编译器可以评估一个分支抛出异常而不是用它来形成返回类型的最小边界,但是如果你在方法中移出抛出代码则不会:因为它可以被覆盖,编译器不能确保它实际上永远不会回来。

你对Break结构的使用似乎很混乱:它已经提供了一个break方法,没有必要提供你自己的方法,除非你想抛出你的异常,这将使得使用Break变得不必要。

然后你会选择几个选项,因为我认为在你的情况下不需要使用Break。

1)只需在失败时抛出异常

def myFunc(x: Int): String = {
  if (x > 10) {
    "I am fine"
  } else {
    throw new RuntimeException("Break happened")
  }
}

def usemyFunc(): Unit = {
  try {
    println("myFunc(11) is " + myFunc(11))
    println("myFunc(5) is " + myFunc(5))
  } catch {
    case e: Throwable => println("myFunc failed with " + e)
  }
}

2)使用Try类(可从Scala 2.10获得)返回值或异常。这与之前的建议不同,因为它强制调用者检查结果并检查值是否可用,但是使得使用结果有点麻烦

import scala.util.Try

def myFunc(x: Int): Try[String] = {
  Try {
    if (x > 10) {
      "I am fine"
    } else {
      throw new RuntimeException("Break happened")
    }
  }
}

def useMyFunc(): Unit = {
  myFunc match {
    case Try.Success(s) => println("myFunc succeded with " + s)
    case Try.Failure(e) => println("myFunc failed with " + e)
  }
}

3)如果抛出的异常不相关,则可以使用Option类。 您可以看到使用Options的多种方式如何相互关联 这很棒cheat sheet

def myFunc(x: Int): Option[String] = {
  if (x > 10) {
    Some("I am fine") /* Some(value) creates an Option containing value */
  } else {
    None /* None represents an Option that has no value */ 
  }
}

/* There are multiple ways to work with Option instances.
   One of them is using pattern matching. */

def useMyFunc(): Unit = {
  myFunc(10) match {
    case Some(s) => println("myFunc succeded with " + s)
    case None => println("myFunc failed")
  }
}

/* Another one is using the map, flatMap, getOrElse, etc methods.
   They usually take a function as a parameter, which is only executed
   if some condition is met.

   map only runs the received function if the Option contains a value,
   and passes said value as a parameter to it. It then takes the result
   of the function application, and creates a new Option containing it.

   getOrElse checks if the Option contains a value. If it does, it is returned
   directly. If it does not, then the result of the function passed to it
   is returned instead.

   Chaining map and getOrElse is a common idiom meaning roughly 'transform the value
   contained in this Option using this code, but if there is no value, return whatever
   this other piece of code returns instead'.
*/

def useMyFunc2(): Unit = {
  val toPrint = myFunc(10).map{ s =>
    "myFunc(10) succeded with " + s
  }.getOrElse{
    "myFunc(10) failed"
  }

  /* toPrint now contains a message to be printed, depending on whether myFunc
     returned a value or not. The Scala compiler is smart enough to infer that
     both code paths return String, and make toPrint a String as well. */

  println(toPrint)
}