做最多N次或直到Scala满足条件

时间:2013-11-07 21:48:14

标签: scala functional-programming

我有一些Scala代码应该在网络服务中查询某个令牌,然后将该令牌与预期的令牌进行比较。我想继续查询预期的令牌,直到我找到它,或者直到我做了N次尝试失败。

以下是如何以Java风格的方式完成:

def keepLooking(expectedToken: String, maxTries: Int) {
  var tries = 0
  var token = ""
  do {
    Thread.sleep(tries * 1000) // don't overwhelm the service by calling it too fast!

    token = makeSomeNetworkCall()
    tries += 1
  } while (tries <= maxTries && token != expectedToken)
}

我想在功能上做得更多。我有一个想法:

1.to(maxTries) map { tryNum =>
  Thread.sleep(tryNum - 1 * 1000) // don't overwhelm the service by calling it too fast!
  makeSomeNetworkCall()
} exists (_ == expectedToken)

但这提出了两个问题:

  1. map很懒,所以exists应该短路吧?如果我在第二次通话中找到我的令牌,我不想拨打10个网络电话。
  2. 有没有更惯用的方式来实现我的目标?

4 个答案:

答案 0 :(得分:4)

回答1:

如果您将map转换为Range,则

Stream只是懒惰:

(1 to 10).toStream map (i => { println(i); i }) exists (_ == 2)
// will print
// 1
// 2

答案 1 :(得分:3)

map是否懒惰,通常取决于集合类型。为了确保它是懒惰的,你可以使用.toStream.view(后者不会缓存其结果,所以我通常选择这样做。)

除此之外,对我来说似乎没问题,但是对于你真的需要从响应中获取一些数据的情况它不会起作用(我知道这不是你在这个问题中需要的,但是我'我试图在这里更广泛地思考。)

答案 2 :(得分:1)

map上的{p> Range不是懒惰的,因此会进行maxTries个网络呼叫,然后在结果中查找expectedToken。相反,你可以使用一些懒惰的结构。一个Iterator,例如:

Iterator.fill(maxTries) {
  makeSomeNetworkCall()
}.exists(_ == expectedToken)

答案 3 :(得分:1)

关于问题2,您可以创建自己的Stream

scala>  val keepLooking: Stream[Tuple2[Int, String]] = (0, "a") #:: (1, "aa") #:: keepLooking.tail.map { n => (n._1 + 1, n._2 + "a")}
keepLooking: Stream[(Int, String)] = Stream((0,a), ?)

scala> keepLooking.take(20).find(_._2 == "aaa")
res0: Option[(Int, String)] = Some((2,aaa))

scala> keepLooking.take(20).find(_._2 == "xxx")
res1: Option[(Int, String)] = None

这是一个简化的例子。在您的情况下,您会将"a""aa"n._2 + "a"替换为makeSomeNetworkCall()我假设将token作为String返回。然后,您可以懒惰地使用maxTriestake(n)并使用find查看expectedToken是否存在。