Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala bufferedIterator incrementing head in inner function

Tags:

scala

Hi I'm seeing what I believe is odd behaviour in scala. Calling head on a bufferedIterator seems to be incrementing the head in a inner function. Either my expetations are wrong in which case why is the output correct. Or is the output wrong?

given:

import scala.io.Source

val source = Source.fromString("abcdef")

val buff1 = source.buffered;

println("outer head 1: " +buff1.head)
println("outer head 2: " +buff1.head)

def readLine():List[String] = {
  def buffered = source.buffered
  def readLine(tokens:List[String] , partialToken:String):List[String] = {
    println("head1 " + buffered.head)
    println("head2 " + buffered.head)
    return Nil;
  }
  return (readLine(Nil, ""));
}

readLine();

The expected output of this to me is

outer head 1: a
outer head 2: a
head1: a
head2: a

actual output is as follows.

outer head 1: a
outer head 2: a
head1 b
head2 c
like image 268
Wes Avatar asked Jun 30 '26 13:06

Wes


2 Answers

scala.io.Source is and behaves like an Iterator[Char]. So you must make sure not to use it in several places at once: Iterator.next is called 3 times from 3 different BufferedSource in your example, hence the different values you get out of it:

  1. buff1.head: the buffered source has not buffered anything yet, so asking for head here calls next on the inner source, hence the first a.
  2. buff1.head again: here the head has already been buffered, so you get a and the inner source isn't changed.
  3. buffered.head: since buffered is a def, this is equivalent to source.buffered.head. This new buffered source has not buffered anything yet, so asking for head retrieves an element from the inner source, hence the b.
  4. buffered.head: this creates yet another buffered source, same as above, and you get c.

The bottom line is: if you call source.buffered, never use source again directly, and do not call it several times either.

Your example can be fixed by calling buffered immediately:

val source = Source.fromString("abcdef").buffered

You could also turn def buffered = into val buffered = to make sure source.buffered is not called several times.

like image 86
gourlaysama Avatar answered Jul 02 '26 13:07

gourlaysama


Calling head on a bufferedIterator seems to be incrementing the head in a inner function.

Note: (July 2016 3 years later)

Commit 11688eb shows:

SI-9691 BufferedIterator should expose a headOption

This exposes a new API to the BufferedIterator trait.
It will return the next element of an iterator as an Option.

The return will be Some(value) if there is a next value, and None if there is not a next element.

That should help avoid any kind of increment.

like image 44
VonC Avatar answered Jul 02 '26 13:07

VonC



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!