Как можно переписать код Stream iterate-takeWhile без побочных эффектов? - PullRequest
1 голос
/ 16 мая 2019

Я написал крошечный веб-сайт для проверки рейтинга Google в Scala 2.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 возвращал мне result непосредственно, то есть позицию или рейтинг, где появляется целевой веб-сайт. Я не могу перебирать одну за другой, потому что код получает каждую страницу из 10 результатов за раз, обрабатывает их страницы и ищет целевой веб-сайт в каждой группе из 10 результатов.

1 Ответ

3 голосов
/ 16 мая 2019

Вы могли бы просто разделить ваш конвейер на 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?

Вы также должны знать, что я просто избавляюсь от назначения изменяемой переменной,но ваш код все равно будет иметь побочные эффекты, потому что вы выполняете сетевые вызовы.

Вам следует рассмотреть возможность использования Future или какой-либо монады IO для обработки этих вызовов.

...