سؤال

In a Stackoverflow post about the creation of Fibonacci numbers I found the method #:: (What is the fastest way to write Fibonacci function in Scala?). In ScalaDocs I found this entry (see here, 1) describing the hash colon colon method as An extractor that allows to pattern match streams with #::.

I realized that I can use the fibonacci function like this

def fibonacci: Stream[Long] = {
    def tail(h: Long, n: Long): Stream[Long] = h #:: tail(n, h + n)
    tail(0, 1)
}
fibonacci(10)       //res4: Long = 55
  • How should I understand the ScalaDocs explanation? Can you give an additional example?

  • Why it was not necessary to define a parameter in the fibonacci function above?

هل كانت مفيدة؟

المحلول

The method #:: is defined for Streams. It is similar to the :: method for Lists. The main difference between a List and a Stream is that the elements of a Stream are lazy evaluated.

There's some scala magic happens on the last line. Actually, first you're evaluating the fibonacci expression, and it returns a Stream object. The first and the second elements of this stream are 0 and 1, as follows from the third line of your example, and the rest of the Stream is defined via recursive call. And then you're extracting tenth element from the stream, and it evaluates to 55.

In the code below, I show similar access to the fourth List's element

val list = List(1,2,3,4,5)
println(list(3)) // prints 4

In a nutshell, think about Streams as infinite Lists. You can find more about Streams here http://www.scala-lang.org/api/current/index.html#scala.collection.immutable.Stream

نصائح أخرى

In your example h #:: tail(n, h + n) creates a new stream, where the h is the head of the stream and tail(n, h + n) a stream which will be evaluated lazily.

Another (and maybe easier) example would be to define natural numbers as a stream of BigInt.

def naturalNumbers = {
  def next(n: BigInt) : Stream[BigInt] = n #:: next(n + 1)
  next(0)
}

println(naturalNumbers) would result in printing Stream(0, ?), because the head is strict, meaning that it will be always evaluated. The tail would be next(1), which is only evaluated when needed.

In your example fibonacci(10) is syntactic sugar for fibonacci.apply(10) which is defined in the Stream class and yields the element with the index in the stream.

You can also do a lot of others things with streams. For example get the first fibonacci number that is greater than 100: fibonacci.dropWhile(_ <= 100).head or just print the first 100 fibonacci numbers println(fibonacci.take(100).toList)

The quick answer to #2 is that fibonacci(10) isn't a function call with parameters, it's a function call with no parameters followed by an invocation of whatever is returned with the parameter "10".

It would have been easier to understand if written like this:

scala> val s = fibonacci
s: Stream[Long] = Stream(0, ?)

scala> s(10)
res1: Long = 55
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top