11

Scala 中的一个非常新手的问题 - 我如何在 Scala 中执行“重复函数,直到返回的内容符合我的标准”?

鉴于我有一个函数要调用,直到它返回结果,例如,定义如下:

def tryToGetResult: Option[MysteriousResult]

我想出了这个解决方案,但我真的觉得它很难看:

var res: Option[MysteriousResult] = None
do {
  res = tryToGetResult
} while (res.isEmpty)
doSomethingWith(res.get)

或者,同样丑陋:

var res: Option[MysteriousResult] = None
while (res.isEmpty) {
  res = tryToGetResult
}
doSomethingWith(res.get)

我真的觉得有一个解决方案,没有和没有那么多手动检查是否为空var的麻烦。Option

相比之下,我在这里看到的 Java 替代方案似乎更干净:

MysteriousResult tryToGetResult(); // returns null if no result yet

MysteriousResult res;
while ((res = tryToGetResult()) == null);
doSomethingWith(res);

雪上加霜,如果我们不需要doSomethingWith(res)并且只需要从这个函数中返回它,Scala vs Java 看起来像这样:

斯卡拉

def getResult: MysteriousResult = {
  var res: Option[MysteriousResult] = None
  do {
    res = tryToGetResult
  } while (res.isEmpty)
  res.get
}

爪哇

MysteriousResult getResult() {
    while (true) {
        MysteriousResult res = tryToGetResult();
        if (res != null)  return res;
    }
}
4

2 回答 2

15

您可以使用Stream'continually方法精确地做到这一点:

val res = Stream.continually(tryToGetResult).flatMap(_.toStream).head

或者(可能更清楚):

val res = Stream.continually(tryToGetResult).dropWhile(!_.isDefined).head

这种方法相对于显式递归(除了简洁)的一个优点是它更容易修补。例如,假设我们决定只想尝试一千次得到结果。如果在此之前出现了一个值,我们希望它包含在 aSome中,如果没有,我们希望 a None。我们只是在上面的代码中添加了几个字符:

Stream.continually(tryToGetResult).take(1000).flatMap(_.toStream).headOption

我们有我们想要的。(注意Stream是惰性的,所以即使take(1000)存在 ,如果一个值在 3 次调用 后出现tryToGetResult,它只会被调用 3 次。)

于 2012-11-22T01:51:39.307 回答
7

执行这样的副作用让我有点死在里面,但是这个怎么样?

scala> import scala.annotation.tailrec
import scala.annotation.tailrec

scala> @tailrec
     | def lookupUntilDefined[A](f: => Option[A]): A = f match {
     |   case Some(a) => a
     |   case None => lookupUntilDefined(f)
     | }
lookupUntilDefined: [A](f: => Option[A])A

然后像这样调用它

scala> def tryToGetResult(): Option[Int] = Some(10)
tryToGetResult: ()Option[Int]

scala> lookupUntilDefined(tryToGetResult())
res0: Int = 10

您可能需要提供lookupUntilDefined一个附加参数,以便在 f 从未定义的情况下最终停止。

于 2012-11-22T01:17:17.760 回答