9

A friend of mine is learning Scala and wrote this simple code to keep track of the longest line in a file:

val longest = (filename:String) => {
  val is = new FileInputStream(filename)
  val buf = new Array[Byte](1024)
  var longest=0 //keep track of the longest line
  var lastPos=0
  var read=0
  try {
    read = is.read(buf)
    while (read > 0) {
      for (i<-0 until read) {
        if (buf(i) == '\n') {
          val size=i-lastPos-1
          lastPos=i
          if (size>longest) {
            longest=size
          }
        }
      }
      lastPos-=buf.length
      read=is.read(buf)
    }
  } finally {
    is.close()
  }
  longest
}

I'm new to Scala too, but I'm pretty sure there's a lot of room for flatMaps and other functions in this code.

Could someone post a functional version of this?

0

3 Answers 3

24

An alternative implementation:

def longest(filename: String) =
  Source.fromFile(filename).getLines.map(_.size).max

Brief explanation:

  • getLines returns an iterator of the lines in the file;
  • map(_.size), equivalent to map(line => line.size), returns a new iterator of the line lengths
  • max returns the greatest line length.
Sign up to request clarification or add additional context in comments.

5 Comments

How efficient is this code. I may "guess" it iterates the file ( or keeps it in memory ) three times, 1 for getLines, 1 for map , and last one to get the max, while the original code does only one pass, but I may be wrong of course.
The file will only be iterated once. When you call map, it does not pull any values from the iterator; it stores the _.size function and creates a new iterator which will apply that function to each output of the first iterator when called. Calling max finally forces the iteration.
Got it, that's why it is called BufferedSource :) What about .max?
I meant to say max at the end of that comment, edited it now.
So.. that makes it 1 iteration total? :-/
13
val longest = (filename: String) =>
  io.Source.fromFile(filename).getLines.maxBy(_.length).length

4 Comments

Admittedly your solution with maxBy is more pleasant than mine, +1.
Hello @missingfaktor this looks more efficient isn't? I understand while it read the lines it applies the _.length code but I don't get why does it returns "length" instead of max , shouldn't that be ...minBy(_.length)[0] or something like that?
@Oscar: Ben's code should be just as fast (given that getLines returns an iterator).
@Oscar: getLines gives you an Iterator[String]. maxBy returns the line for which the function _.length returns maximum value i.e. the longest line. At last, we take the length of the longest line.
5

Yes, this code is painfully imperative. In Scala the rough equivalent would be (!):

def longest(fileName: String) = 
  Source.fromFile(fileName).getLines().max(Ordering.fromLessThan[String](_.size < _.size)).size

Guess it wouldn't hurt to provide some explanation:

def longest(fileName: String) = Source.
    fromFile(fileName).     //file contents abstraction
    getLines().     //iterator over lines
    max(        //find the max element in iterated elements
        Ordering.fromLessThan[String](_.size < _.size)  //however, use custom comparator by line size
    ).size  //max() will return the line, we want the line length

Of course TMTOWTDI in Scala.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.