问题
What is the best style in scala for "advanced iteration" over collection of smth. In which cases I should use for-comprehension and when I should look for alternative way(in terms of style) of iteration. In Programming in Scala
book there is an example that looks almost as next one:
for{
file <- filesHere
if file.getName.endsWith("txt")
line <- Source.fromFile(file).getLines.toList
if line.trim.matches(pattern)
} println("|" + file + ": " + line.trim)
I've tried to rewrite it using internal iteration and got:
filesHere foreach { file =>
if (file.getName.endsWith("txt")) {
Source.fromFile(file).getLines.toList foreach {line =>
if (line.trim.matches(pattern)) println("|" + file + ": " + line.trim)
}
}
}
However, on the web(including stackoverflow) I've found a lot of articles/posts persuading to use for-comprehension, I don't find it convenient for me to use it. IMHO, internal iteration is much readable and convenient.
What are the best guidelines on this topic?
回答1:
I think it is great that you are learning Scala. There is a wonderful community behind it, very active, and I think that you'll be more-and-more pleased with your decision as time goes on.
For the moment, at least, I think that you should stick to "Programming in Scala" as it is very well written and a terrific introduction to the language. Then, a good place to look will be the Scala api docs themselves as many of the classes/methods contain sample code. Also, the google group is very active (and, as I said, the people there are very friendly).
Keep in mind that the code you provide as an example returns no value, but rather just gives side-effects (in Java, this would have a void
return value). Most of the Scala API is geared around functions with little to no side-effects that return a value (you might have, for example, a side-effect of logging, but idiomatic scala code will minimize side-effects in methods and functions).
Within the Scala API, there are methods that would be the preferred style, if it matches your need. For example, in Scheme you might do an inner loop to iterate, in Scala it might look like this:
val myScores = List(4, 8, 6, 1, 2, 4, 9, 8)
def max(ls: List[Int]): Int = {
def iter(acc: Int, rem: List[Int]): Int = {
if (rem.isEmpty) acc
else if (rem.head > acc) iter(rem.head, rem.tail)
else iter(acc, rem.tail)
}
iter(ls.head, ls)
}
But I don't need to write that function because max
is already implemented in the list API:
myScores.max
So, if what you want to do is already implemented by a library, you should use that. max()
is a trivial example, but there are much more complex transformations to be found in the Scala API and scalaz
A for
-comprehension (the original topic of your post) is a combination of flatMap
, map
, and filter
. If you are doing something fairly simple, then both versions will be just as readable, but if you are doing a long chain of flatMap
, map
, and filter
strung together to create a complex transformation, then a for
-comprehension will be more readable.
EDIT:
Specific to your question, I think the for
-comprehension version is easier to read:
val filterMapFlatmapVersion: List[String] =
fileNames.filter(_.endsWith("txt")).flatMap { file =>
io.Source.fromFile(file).getLines().toList.map(_.trim).filter(_ == pattern)
}
val forVersion: List[String] =
for {
fileName <- fileNames
if fileName.endsWith("txt")
line <- io.Source.fromFile(fileName).getLines()
if line.trim().matches(pattern)
} yield line.trim
回答2:
I would expect for-comprehensions to be the preferred approach in Scala. It's worth noting that a for-comprehension is really syntactic sugar for a map
and filter
.
来源:https://stackoverflow.com/questions/18877351/scala-style-for-vs-foreach-filter-map-and-others