Scala считать количество итераций внутреннего цикла - PullRequest
1 голос
/ 02 января 2011

Если бы я хотел перечислить итерации внутреннего цикла в Scala, как бы я подошел к этому в функциональном стиле?

Например, как бы я переписал следующий код:

val documents = List("a" :: "b" :: Nil, "aa" :: "bb" :: Nil, "aaa" :: Nil)
var currentPageNumber = 0

documents.foreach { doc =>
  for (page <- doc) {
    currentPageNumber += 1
    println("%d: %s".format(currentPageNumber.head, page)
    // do something with page
  }
}

Я мог бы избавиться от var, используя val currentPageNumber = Iterator.from(0), но это все еще означает, что мне действительно нужно объявить его вне цикла.

Есть ли уловка, которая не выставит currentPageNumber навнешняя область и - так же, как счетчик zipWithIndex - существует только внутри цикла?

Редактировать:

Я также нашел версию, использующую scanLeft, ноЯ думаю, что это довольно запутанно.Но, возможно, кто-то может как-то оптимизировать его.

documents.scanLeft(0) { case (cnt, doc) =>
  doc.zip(Iterator.from(cnt + 1).toIterable).map { case(page, cnt) =>
    println("%d: %s".format(cnt, page))
    cnt
  } last
}

Собственное решение (пока):

Спасибо всем, я думаю, что я имел в виду что-то вроде

documents.flatMap{ doc =>
  for (page <- doc) yield {
    currentPageNumber: Int =>
      "%d: %s".format(currentPageNumber, page)
}}.zipWithIndex.map (t => t._1(t._2 + 1)) map(println)

Итак, хитрость заключается в том, чтобы оставить currentPageNumber нерешенным до тех пор, пока человек не сможет применить zipWithIndex.

Ответы [ 4 ]

6 голосов
/ 02 января 2011

Я думаю, что используемая вами императивная версия, вероятно, лучше, поскольку у printf есть побочные эффекты, но если вы хотите что-то функциональное, вы можете сделать это:

documents.foldLeft(1){ (docStartingPage,doc) =>
   doc.foldLeft(docStartingPage){ (currentPageNumber, page) =>
      printf("%d: %s\n", currentPageNumber, page)
      currentPageNumber + 1
   }
}

К сожалению,номер должен быть доступен для внешнего цикла, если он снова будет правильно отображаться во внутреннем цикле.

Вы также можете попробовать следующее, хотя вы потеряете любую информацию о том, где начинаются и заканчиваются документы.

for ( (page,pageNumber) <- documents.flatten.zipWithIndex )
  printf("%d: %s\n", pageNumber + 1, page)
4 голосов
/ 03 января 2011

Вы упоминаете, что не можете сгладить, потому что это не список списка.Однако, если они поддерживают flatMap и map, то вы можете сгладить.Например:

documents.view.flatMap(_ map identity).            // flatten
zipWithIndex.                                      // get the index
map(t => t._2 + 1 -> t._1).                        // adjust page number
map(("%d: %s".format(_: Int, _: String)).tupled).  // format output
foreach(println)                                   // tah dah

Шаги три и четыре можно объединить в один шаг.На самом деле, вы могли бы добавить и пятый шаг, но об этом мы говорим, поэтому я не думаю, что это имеет большое значение.

Если, однако, вы ограничены foreach ..ну, тогда это все еще Traversable.Вы можете сделать .toStream и получить все вышеперечисленное (и удалить view):

documents.toStream.flatMap(_.toStream map identity).
zipWithIndex.                                     
map(t => t._2 + 1 -> t._1).                       
map(("%d: %s".format(_: Int, _: String)).tupled). 
foreach(println)                                  
1 голос
/ 03 января 2011

Вы можете получить инициализацию currentPageNumber во внешнем foreach:

val documents = List("a" :: "b" :: Nil, "aa" :: "bb" :: Nil, "aaa" :: Nil)

documents.foreach {
  var currentPageNumber = 0
  doc => for(page <- doc) {
    currentPageNumber += 1
    printf("%d: %s%n", currentPageNumber, page)
    // do something with page
  }
}
1 голос
/ 02 января 2011

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

val documents = List("a" :: "b" :: Nil, "aaa" :: Nil)
val lettercount = {
  var currentPageNumber = 0
  documents.map(_.map(s => {
    currentPageNumber += 1
    println("Page " + currentPageNumber + " is " + s)
    s.length
  }).sum).sum
}
// currentPageNumber = 7  // Uncommented, this would be an error
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...