这段 Stream iterate-takeWhile 代码怎么改写无副作用呢?
How can this Stream iterate-takeWhile code be rewritten without side effects?
我用 Scala 2 编写了一个小型网站 Google 排名检查器。12.x 使用页面抓取来查找给定搜索词的网站排名。我想使用 Scala 的 Stream 构建它,这是代码的控制结构模拟。但是,我找不到一种没有副作用的方法来重写它,换句话说,不使用任何 var
.
def main(args: Array[String]): Unit = {
val target = 22 // normally this would be the website domain name
val inf = 100 // we don't care for ranks above this value
var result: Option[Int] = None // <============= Side effects! how to rewrite it?
Stream.iterate(0)(_ + 10).takeWhile { i =>
// assume I'm page-scraping Google with 10 results per page
// and need to find the rank or position where the target
// website appears
for (j <- i until (i + 10)) {
// check whether the website was found
if (j == target) {
result = Some(j) // <============= Side effects! how to rewrite it?
}
}
result.isEmpty && i < inf
}.toList
println(result.getOrElse(inf))
}
基本上我希望 Stream
语句直接 return 我 result
,这是目标网站出现的位置或排名。我不能一个一个地迭代,因为代码一次获取每页 10 个结果,页面抓取它们并在每组 10 个结果中搜索目标网站。
您可以将管道拆分为 map
和 dropWhile
(替换为 takeWhile
):
val target = 22 // normally this would be the website domain name
val inf = 100 // we don't care for ranks above this value
val result = Stream.iterate(0)(_ + 10).map { i =>
//or maybe just use find?
val r = Stream.range(i-10, i).dropWhile(_ != target).headOption
(r,i) //we pass result with index for dropWhile
}.dropWhile{
case (r, i) => r.isEmpty && i < inf //drop while predicate is false
}.map(_._1) //take only result
.head //this will throw an exception if nothing is found, maybe use headOption?
你也应该知道,我只是摆脱了分配一个可变变量,但你的代码仍然会有 side-effects 因为你正在进行网络调用。
您应该考虑使用 Future
或某种 IO
monad 来处理这些调用。
我用 Scala 2 编写了一个小型网站 Google 排名检查器。12.x 使用页面抓取来查找给定搜索词的网站排名。我想使用 Scala 的 Stream 构建它,这是代码的控制结构模拟。但是,我找不到一种没有副作用的方法来重写它,换句话说,不使用任何 var
.
def main(args: Array[String]): Unit = {
val target = 22 // normally this would be the website domain name
val inf = 100 // we don't care for ranks above this value
var result: Option[Int] = None // <============= Side effects! how to rewrite it?
Stream.iterate(0)(_ + 10).takeWhile { i =>
// assume I'm page-scraping Google with 10 results per page
// and need to find the rank or position where the target
// website appears
for (j <- i until (i + 10)) {
// check whether the website was found
if (j == target) {
result = Some(j) // <============= Side effects! how to rewrite it?
}
}
result.isEmpty && i < inf
}.toList
println(result.getOrElse(inf))
}
基本上我希望 Stream
语句直接 return 我 result
,这是目标网站出现的位置或排名。我不能一个一个地迭代,因为代码一次获取每页 10 个结果,页面抓取它们并在每组 10 个结果中搜索目标网站。
您可以将管道拆分为 map
和 dropWhile
(替换为 takeWhile
):
val target = 22 // normally this would be the website domain name
val inf = 100 // we don't care for ranks above this value
val result = Stream.iterate(0)(_ + 10).map { i =>
//or maybe just use find?
val r = Stream.range(i-10, i).dropWhile(_ != target).headOption
(r,i) //we pass result with index for dropWhile
}.dropWhile{
case (r, i) => r.isEmpty && i < inf //drop while predicate is false
}.map(_._1) //take only result
.head //this will throw an exception if nothing is found, maybe use headOption?
你也应该知道,我只是摆脱了分配一个可变变量,但你的代码仍然会有 side-effects 因为你正在进行网络调用。
您应该考虑使用 Future
或某种 IO
monad 来处理这些调用。