如果我想在Scala中枚举内部循环的迭代,我将如何以函数式方式处理它?</ p>
E.g。我将如何重写以下代码:
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
。
答案 0 :(得分:6)
我认为你使用的命令式版本可能更好,因为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)
答案 1 :(得分:4)
你提到你不能扁平化,因为它不是列表清单。但是,如果他们支持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)
答案 2 :(得分:1)
如果您希望它快,只需添加大括号即可添加另一个范围。您也可以从该范围返回值,因此确实没有缺点:
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
答案 3 :(得分:1)
您可以在外部foreach中拉出currentPageNumber的初始化:
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
}
}