【问题标题】:Return from nested condition, for loop, try/catch block in Scala从嵌套条件返回,for 循环,在 Scala 中的 try/catch 块
【发布时间】:2013-02-11 17:22:09
【问题描述】:

我有一个嵌套的条件块、一个 for 循环和一个 try/catch 块来返回一个元组:

  val (a, b) = {
    if (...) {
      for (...) {
        try {
          getTuple(conf)
        } catch {
          case e: Throwable => println(...)
        }
      }
      sys.exit
    } else {
      try {
        getTuple(userConf)
      } catch {
        case e: Throwable => println(...); sys.exit
      }
    }
  }

如果if 条件匹配,我想尝试x 个不同的conf 配置。当getTuple 抛出异常时,尝试下一个。当getTuple 没有抛出异常时,用结果填充元组。 getTuple 返回元组 (a,b)

问题:然而,for 循环不会在getTuple抛出异常时退出。我也试过break,但这不起作用,因为它应该返回元组,而不仅仅是退出for循环。

我怎样才能让它工作?

【问题讨论】:

  • scala 只有for 理解。在无限循环中几乎是不可能的。我可能会在无限流中循环!其次,在评估 for 理解之后的 if 语句中的 sys.exit 调用放错了位置!
  • for 循环不是无限的,它只会循环几次。
  • 我不明白。 for 循环不是无限的(评论)和 然而,for 循环永远不会退出(问题)似乎有些矛盾...
  • 对不起,我改写了那部分。
  • @str 您能否发布一个近似的for 循环,您可以从中获得要测试的配置?这将有助于找到打破循环的正确方法。

标签: scala for-loop nested return try-catch


【解决方案1】:

getTuple 应该评估为Option[Tuple2[T,U]],而不是抛出异常,它具有更多含义并且不会破坏程序的流程。

这样,你可以有一个这样的:

val tuples: List[Option[Tuple2[T,U]] = for {
  c <- configs
} yield getTuple(c)

val firstWorkingConfig: Option[Tuples2[T,U]] = tuples.flatten.headOption

// Exit the program if no config is okay
firstWorkingConfig.getOrElse {
  sys.exit
}

希望对你有帮助

【讨论】:

  • 我更喜欢 configs.toStream.map(c =&gt; getTuple(c)) 而不是这里的 for 循环。
  • 好主意,我想到了 Streams,但没有很快想出解决方案,所以我去理解 ;)
【解决方案2】:

我建议使用 Scala 的更多功能特性,而不是处理循环和异常。

假设我们有一个函数在某些输入上抛出异常:

def myfn(x: Double): Double =
  if (x < 0)
    throw new Exception;
  else
    Math.sqrt(x);

我们可以重构它以返回Option

def optMyfn(x: Double): Option[Double] =
  if (x < 0)
    None;
  else
    Some(Math.sqrt(x));

或者如果我们不想(或无法修改原始代码),我们可以简单地使用Exception.Catcher 包装它:

def optMyfn(x: Double): Option[Double]
  = scala.util.control.Exception.allCatch.opt(myfn(x));

现在让我们有一个数字序列,我们想找到函数成功的第一个数字:

val testseq: Seq[Double] = Seq(-3.0, -2.0, 2.0, 4.0, 5.0);

我们可以利用Scala的函数特性,将函数应用于所有元素,并找到第一个Some结果为

testseq.toStream.map(optMyfn _).flatten.headOption

不使用toStream 也可以,但我们会不必要地在所有元素上调用optMyfn,而不仅仅是我们需要找到第一个成功结果的那些元素——流进行计算lazy

(另一种选择是使用views,例如

testseq.view.map(optMyfn _).collectFirst({ case Some(x) => x })

.)

【讨论】:

    【解决方案3】:

    sys.exit 调用在 if 语句中有点错位。它应该在catch 子句中,以允许在尝试成功时返回 tupe。

    【讨论】:

    • 当抛出异常时,它应该只是重试而不退出。只有在尝试了所有可能性(即离开for 循环)都没有成功时,它才应该退出。所以sys.exit 的位置是正确的。
    • 第二个是第一个......不。即使找到配置后,for循环也会继续循环,结果是tuples类型的list,而预期的类型是Tuple2
    【解决方案4】:

    如果你想循环直到你得到一个工作元组,while 循环更有意义。 for 循环的语义是评估您正在迭代的所有元素的主体 。由于您的目标是在满足条件的第一个之后停止,因此 while 循环似乎更自然。当然还有一些功能性更强的替代品,例如:

    • getTuple 包装在Option 中,其中None 对应于异常。
    • 将对getTuple 的调用放入一些可迭代/可遍历的集合中
    • 获取第一个不是None的元素

    当我解释这个概念时,Romain Sertelon 给出了一个很好的例子......

    【讨论】:

    • 还有一个最大尝试次数,所以for 循环是有意义的。此外,while 循环的问题不会有所不同。
    • 最大尝试次数不是 while 循环的问题,只是扩展您的条件。
    【解决方案5】:

    另一种解决方案是将整个块包装到一个方法中并使用 return 像:

      val (a: Type, b: Type) = setup(...)
    
      def setup(...) : (Type, Type) = {
        if (...) {
          for (...) {
            try {
              return getTuple(...)
            } catch {
              ...
            }
          }
          sys.exit
        } else {
          try {
            return getTuple(...)
          } catch {
            case e: Throwable => println(...); sys.exit
          }
        }
      }
    

    我认为我应该学习更多 Scala 的基础知识,但目前这行得通。

    感谢您的回答。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-01-02
      • 1970-01-01
      • 2020-10-05
      • 1970-01-01
      • 1970-01-01
      • 2012-05-18
      相关资源
      最近更新 更多