简体   繁体   中英

Scala style: for vs foreach, filter, map and others

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?

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

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 .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM