Is this foldRight for this Streams implementation lazy or strict - scala

I am reading Functional Programming in Scala and am on chapter 5, which covers strictness and laziness. I've done all the problems in the book up to 5.13.
One thing I realized as I was working through the problems was that I didn't really understand all the nuances of foldRight until I started doing the problems in this chapter. My question is about the implementation of foldRight given in this chapter.
Let's begin by looking at the implementation for Streams given in this chapter.
import Stream._
trait Stream[+A] {
def toList: List[A] = this match {
case Empty => Nil
case Cons(h, t) => h() :: t().toList
}
def foldRight[B](z: => B)(f: (A, => B) => B): B = // The arrow `=>` in front of the argument type `B` means that the function `f` takes its second argument by name and may choose not to evaluate it.
this match {
case Cons(h,t) => f(h(), t().foldRight(z)(f)) // If `f` doesn't evaluate its second argument, the recursion never occurs.
case _ => z
}
def map[B](f: A => B): Stream[B] =
foldRight(empty[B])((h,t) => cons(f(h), t))
def filter(f: A => Boolean): Stream[A] =
foldRight(empty[A])((h,t) =>
if (f(h)) cons(h, t)
else t)
}
case object Empty extends Stream[Nothing]
case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A]
object Stream {
def cons[A](hd: => A, tl: => Stream[A]): Stream[A] = {
lazy val head = hd
lazy val tail = tl
Cons(() => head, () => tail)
}
def empty[A]: Stream[A] = Empty
def apply[A](as: A*): Stream[A] =
if (as.isEmpty) empty
else cons(as.head, apply(as.tail: _*))
}
In particular, let's zoom in on the definition of foldRight given here:
def foldRight[B](z: => B)(f: (A, => B) => B): B = // The arrow `=>` in front of the argument type `B` means that the function `f` takes its second argument by name and may choose not to evaluate it.
this match {
case Cons(h,t) => f(h(), t().foldRight(z)(f)) // If `f` doesn't evaluate its second argument, the recursion never occurs.
case _ => z
}
From the text in the book, we know that this implementation of foldRight will not evaluate the second argument of the "chain" function unless it is called. Therefore, foldRight can terminate early before evaluating all the elements of the Stream. Consider this implementation of takeWhile in terms of foldRight as an example:
def takeWhile(f: A => Boolean): Stream[A] =
foldRight(empty[A])((h,t) =>
if (f(h)) cons(h,t)
else empty)
This call to foldRight in the definition of takeWhile can terminate early before traversing all the elements of the Stream. So we know foldRight can terminate early. However, this is not the same thing as saying foldRight is lazy. My question is if this implementation of foldRight is itself lazy or strict. That is, does it apply f to every relevant element in the Stream?
For example, let us take this example:
val someStream = Stream.apply(0, 1, 2, 3, 5)
someStream.foldRight(Empty:Stream[Int]){ (e, acc) =>
Cons(()=>e, ()=>acc)
}
What this call to foldRight would normally do for a non-lazy collection such as a list is that it will copy the list. For this call to someStream, however, will it create a copy of all the members of the Stream? Or will it only evaluate the first term?
Recall that the function in foldRight has the following signature: (A, => B) => B. My question really boils down to when acc, the call-by-name second argument of type B, is evaluated. Is it evaluated when f is first called? Or is acc evaluated when ()=>acc is evaluated? Depending on which one it is, we will either evaluate all the members of the Stream or only the first one when we perform the fold.
And just to reiterate, saying that foldRight terminates early and saying that foldRight is lazy are two different things. The discussion in the book makes it clear that the first statement can be true. This foldRight can terminate early without traversing all the elements of the Stream. But is the second statement true? Is the foldRight we have here lazy?
I bring this up because there is a program trace on page 72 that I don't think I fully understand. It is linked to whether foldRight is itself lazy or not.
Stream(1,2,3,4).map(_ + 10).filter(_ % 2 == 0).toList
cons(11, Stream(2,3,4).map(_ + 10)).filter(_ % 2 == 0).toList
Stream(2,3,4).map(_ + 10).filter(_ % 2 == 0).toList
cons(12, Stream(3,4).map(_ + 10)).filter(_ % 2 == 0).toList
12 :: Stream(3,4).map(_ + 10).filter(_ % 2 == 0).toList
12 :: cons(13, Stream(4).map(_ + 10)).filter(_ % 2 == 0).toList
12 :: Stream(4).map(_ + 10).filter(_ % 2 == 0).toList
12 :: cons(14, Stream().map(_ + 10)).filter(_ % 2 == 0).toList
12 :: 14 :: Stream().map(_ + 10).filter(_ % 2 == 0).toList
12 :: 14 :: List()
Notice how in this trace, the first term (the 1) is passed through the map and filter once before any of the subsequent terms are. Since the map and filter are defined in the Stream using foldRight, this has implications for my question.
So, let's summarize my question:
1) Is foldRight in the implementation given lazy or strict? This is a different question from whether it terminates early.
2) For the following example code,
val someStream = Stream.apply(0, 1, 2, 3, 5)
someStream.foldRight(Empty:Stream[Int]){ (e, acc) =>
Cons(()=>e, ()=>acc)
}
When is acc, the call-by-name parameter, evaluated? Is it when f is called and (e, acc) => Cons(()=>e, ()=>acc) is first created? Or is it when ()=>acc is evaluated?
3) Please explain why in the program trace given, the first term is passed through the map and filter first before moving onto the second term.
You are encouraged to use the text of Functional Programming in Scala to make your point.

Related

Understand Stream scala interleaved transformations behavior

I'm reading and having fun with examples and exercises contained in the book Functional Programming in Scala. I'm studing the strictess and laziness chapter talking about the Stream.
I can't understand the output produced by the following code excerpt:
sealed trait Stream[+A]{
def foldRight[B](z: => B)(f: (A, => B) => B): B =
this match {
case Cons(h,t) => f(h(), t().foldRight(z)(f))
case _ => z
}
def map[B](f: A => B): Stream[B] = foldRight(Stream.empty[B])((h,t) => {println(s"map h:$h"); Stream.cons(f(h), t)})
def filter(f:A=>Boolean):Stream[A] = foldRight(Stream.empty[A])((h,t) => {println(s"filter h:$h"); if(f(h)) Stream.cons(h,t) else t})
}
case object Empty extends Stream[Nothing]
case class Cons[+A](h: () => A, t: () => Stream[A]) extends Stream[A]
object Stream {
def cons[A](hd: => A, tl: => Stream[A]): Stream[A] = {
lazy val head = hd
lazy val tail = tl
Cons(() => head, () => tail)
}
def empty[A]: Stream[A] = Empty
def apply[A](as: A*): Stream[A] =
if (as.isEmpty) empty else cons(as.head, apply(as.tail: _*))
}
Stream(1,2,3,4,5,6).map(_+10).filter(_%2==0)
When I execute this code, I receive this output:
map h:1
filter h:11
map h:2
filter h:12
My questions are:
Why map and filter output are interleaved?
Could you explain all steps involved from the Stream creation until the last step for obtaining this behavior?
Where are other elements of the list that pass also filter transformation, so 4 and 6?
The key to understanding this behavior, I think, is in the signature of the foldRight.
def foldRight[B](z: => B)(f: (A, => B) => B): B = ...
Note that the 2nd argument, f, is a function that takes two parameters, an A and a by-name (lazy) B. Take away that laziness, f: (A, B) => B, and you not only get the expected method grouping (all the map() steps before all the filter() steps), they also come in reverse order with 6 processed first and 1 processed last, as you'd expect from a foldRight.
How does one little => perform all that magic? It basically says that the 2nd argument to f() is going to be held in reserve until it is required.
So, attempting to answer your questions.
Why map and filter output are interleaved?
Because each call to map() and filter() are delayed until the point when the values are requested.
Could you explain all steps involved from the Stream creation until the last step for obtaining this behavior?
Not really. That would take more time and SO answer space than I'm willing to contribute, but let's take just a few steps into the morass.
We start with a Stream, which looks likes a series of Cons, each holding an Int and a reference to the next Cons, but that's not completely accurate. Each Cons really holds two functions, when invoked the 1st produces an Int and the 2nd produces the next Cons.
Call map() and pass it the "+10" function. map() creates a new function: "Given h and t (both values), create a new Cons. The head function of the new Cons, when invoked, will be the "+10" function applied to the current head value. The new tail function will produce the t value as received." This new function is passed to foldRight.
foldRight receives the new function but the evaluation of the function's 2nd parameter will be delayed until it is needed. h() is called to retrieve the current head value, t() will be called to retrieve the current tail value and a recursive call to foldRight will be called on it.
Call filter() and pass it the "isEven" function. filter() creates a new function: "Given h and t, create a new Cons if h passes the isEven test. If not then return t." That's the real t. Not a promise to evaluate its value later.
Where are other elements of the list that pass also filter transformation, so 4 and 6?
They are still there waiting to be evaluated. We can force that evaluation by using pattern matching to extract the various Cons one by one.
val c0#Cons(_,_) = Stream(1,2,3,4,5,6).map(_+10).filter(_%2==0)
// **STDOUT**
//map h:1
//filter h:11
//map h:2
//filter h:12
c0.h() //res0: Int = 12
val c1#Cons(_,_) = c0.t()
// **STDOUT**
//map h:3
//filter h:13
//map h:4
//filter h:14
c1.h() //res1: Int = 14
val c2#Cons(_,_) = c1.t()
// **STDOUT**
//map h:5
//filter h:15
//map h:6
//filter h:16
c2.h() //res2: Int = 16
c2.t() //res3: Stream[Int] = Empty

Tail recursive fold on a binary tree in Scala

I am trying to find a tail recursive fold function for a binary tree. Given the following definitions:
// From the book "Functional Programming in Scala", page 45
sealed trait Tree[+A]
case class Leaf[A](value: A) extends Tree[A]
case class Branch[A](left: Tree[A], right: Tree[A]) extends Tree[A]
Implementing a non tail recursive function is quite straightforward:
def fold[A, B](t: Tree[A])(map: A => B)(red: (B, B) => B): B =
t match {
case Leaf(v) => map(v)
case Branch(l, r) =>
red(fold(l)(map)(red), fold(r)(map)(red))
}
But now I am struggling to find a tail recursive fold function so that the annotation #annotation.tailrec can be used.
During my research I have found several examples where tail recursive functions on a tree can e.g. compute the sum of all leafs using an own stack which is then basically a List[Tree[Int]]. But as far as I understand in this case it only works for the additions because it is not important whether you first evaluate the left or the right hand side of the operator. But for a generalised fold it is quite relevant. To show my intension here are some example trees:
val leafs = Branch(Leaf(1), Leaf(2))
val left = Branch(Branch(Leaf(1), Leaf(2)), Leaf(3))
val right = Branch(Leaf(1), Branch(Leaf(2), Leaf(3)))
val bal = Branch(Branch(Leaf(1), Leaf(2)), Branch(Leaf(3), Leaf(4)))
val cmb = Branch(right, Branch(bal, Branch(leafs, left)))
val trees = List(leafs, left, right, bal, cmb)
Based on those trees I want to create a deep copy with the given fold method like:
val oldNewPairs =
trees.map(t => (t, fold(t)(Leaf(_): Tree[Int])(Branch(_, _))))
And then proof that the condition of equality holds for all created copies:
val conditionHolds = oldNewPairs.forall(p => {
if (p._1 == p._2) true
else {
println(s"Original:\n${p._1}\nNew:\n${p._2}")
false
}
})
println("Condition holds: " + conditionHolds)
Could someone give me some pointers, please?
You can find the code used in this question at ScalaFiddle: https://scalafiddle.io/sf/eSKJyp2/15
You could reach a tail recursive solution if you stop using the function call stack and start using a stack managed by your code and an accumulator:
def fold[A, B](t: Tree[A])(map: A => B)(red: (B, B) => B): B = {
case object BranchStub extends Tree[Nothing]
#tailrec
def foldImp(toVisit: List[Tree[A]], acc: Vector[B]): Vector[B] =
if(toVisit.isEmpty) acc
else {
toVisit.head match {
case Leaf(v) =>
val leafRes = map(v)
foldImp(
toVisit.tail,
acc :+ leafRes
)
case Branch(l, r) =>
foldImp(l :: r :: BranchStub :: toVisit.tail, acc)
case BranchStub =>
foldImp(toVisit.tail, acc.dropRight(2) ++ Vector(acc.takeRight(2).reduce(red)))
}
}
foldImp(t::Nil, Vector.empty).head
}
The idea is to accumulate values from left to right, keep track of the parenthood relation by the introduction of a stub node and reduce the result using your red function using the last two elements of the accumulator whenever a stub node is found in the exploration.
This solution could be optimized but it is already a tail recursive function implementation.
EDIT:
It can be slightly simplified by changing the accumulator data structure to a list seen as a stack:
def fold[A, B](t: Tree[A])(map: A => B)(red: (B, B) => B): B = {
case object BranchStub extends Tree[Nothing]
#tailrec
def foldImp(toVisit: List[Tree[A]], acc: List[B]): List[B] =
if(toVisit.isEmpty) acc
else {
toVisit.head match {
case Leaf(v) =>
foldImp(
toVisit.tail,
map(v)::acc
)
case Branch(l, r) =>
foldImp(r :: l :: BranchStub :: toVisit.tail, acc)
case BranchStub =>
foldImp(toVisit.tail, acc.take(2).reduce(red) :: acc.drop(2))
}
}
foldImp(t::Nil, Nil).head
}

Corecursion vs Recursion understanding in scala

if with recursion almost clear, for example
def product2(ints: List[Int]): Int = {
#tailrec
def productAccumulator(ints: List[Int], accum: Int): Int = {
ints match {
case Nil => accum
case x :: tail => productAccumulator(tail, accum * x)
}
}
productAccumulator(ints, 1)
}
I am not sure about to the corecursion. According to the Wikipedia article, "corecursion allows programs to produce arbitrarily complex and potentially infinite data structures, such as streams". For example construction like this
list.filter(...).map(...)
makes to posible prepare temporary streams after filter and map operations.
after filter stream will be collect only filtered elements, and next in the map we will change elements. Correct?
Do functional combinators use recursion executions for map filter
Does any body have good example in Scala "comparing recursion and corecursion"?
The simplest way to understand the difference is to think that recursion consumes data while corecursion produces data. Your example is recursion since it consumes the list you provide as parameter. Also, foldLeft and foldRight are recursion too, not corecursion. Now an example of corecursion. Consider the following function:
def unfold[A, S](z: S)(f: S => Option[(A, S)]): Stream[A]
Just by looking at its signature you can see this function is intended to produce an infinite stream of data. It takes an initial state, z of type S, and a function from S to a possible tuple that will contain the next state and the actual value of the stream, that is of type A. If the result of f is empty (None) then unfold stops producing elements otherwise it goes on passing the next state and so on. Here is its implementation:
def unfold[S, A](z: S)(f: S => Option[(A, S)]): Stream[A] = f(z) match {
case Some((a, s)) => a #:: unfold(s)(f)
case None => Stream.empty[A]
}
You can use this function to implement other productive functions. E.g. the following function will produce a stream of, at most, numOfValues elements of type A:
def elements[A](element: A, numOfValues: Int): Stream[A] = unfold(numOfValues) { x =>
if (x > 0) Some((element, x - 1)) else None
}
Usage example in REPL:
scala> elements("hello", 3)
res10: Stream[String] = Stream(hello, ?)
scala> res10.toList
res11: List[String] = List(hello, hello, hello)

Cartesian Product and Map Combined in Scala

This is a followup to: Expand a Set of Sets of Strings into Cartesian Product in Scala
The idea is you want to take:
val sets = Set(Set("a","b","c"), Set("1","2"), Set("S","T"))
and get back:
Set("a&1&S", "a&1&T", "a&2&S", ..., "c&2&T")
A general solution is:
def combine[A](f:(A, A) => A)(xs:Iterable[Iterable[A]]) =
xs.reduceLeft { (x, y) => x.view.flatMap {a => y.map(f(a, _)) } }
used as follows:
val expanded = combine{(x:String, y:String) => x + "&" + y}(sets).toSet
Theoretically, there should be a way to take input of type Set[Set[A]] and get back a Set[B]. That is, to convert the type while combining the elements.
An example usage would be to take in sets of strings (as above) and output the lengths of their concatenation. The f function in combine would something of the form:
(a:Int, b:String) => a + b.length
I was not able to come up with an implementation. Does anyone have an answer?
If you really want your combiner function to do the mapping, you can use a fold but as Craig pointed out you'll have to provide a seed value:
def combine[A, B](f: B => A => B, zero: B)(xs: Iterable[Iterable[A]]) =
xs.foldLeft(Iterable(zero)) {
(x, y) => x.view flatMap { y map f(_) }
}
The fact that you need such a seed value follows from the combiner/mapper function type (B, A) => B (or, as a curried function, B => A => B). Clearly, to map the first A you encounter, you're going to need to supply a B.
You can make it somewhat simpler for callers by using a Zero type class:
trait Zero[T] {
def zero: T
}
object Zero {
implicit object IntHasZero extends Zero[Int] {
val zero = 0
}
// ... etc ...
}
Then the combine method can be defined as:
def combine[A, B : Zero](f: B => A => B)(xs: Iterable[Iterable[A]]) =
xs.foldLeft(Iterable(implicitly[Zero[B]].zero)) {
(x, y) => x.view flatMap { y map f(_) }
}
Usage:
combine((b: Int) => (a: String) => b + a.length)(sets)
Scalaz provides a Zero type class, along with a lot of other goodies for functional programming.
The problem that you're running into is that reduce(Left|Right) takes a function (A, A) => A which doesn't allow you to change the type. You want something more like foldLeft which takes (B, A) ⇒ B, allowing you to accumulate an output of a different type. folds need a seed value though, which can't be an empty collection here. You'd need to take xs apart into a head and tail, map the head iterable to be Iterable[B], and then call foldLeft with the mapped head, the tail, and some function (B, A) => B. That seems like more trouble than it's worth though, so I'd just do all the mapping up front.
def combine[A, B](f: (B, B) => B)(g: (A) => B)(xs:Iterable[Iterable[A]]) =
xs.map(_.map(g)).reduceLeft { (x, y) => x.view.flatMap {a => y.map(f(a, _)) } }
val sets = Set(Set(1, 2, 3), Set(3, 4), Set(5, 6, 7))
val expanded = combine{(x: String, y: String) => x + "&" + y}{(i: Int) => i.toString}(sets).toSet

Step-by-step explanation of Scala syntax used in Wikipedia quicksort example

I am trying to understand the Scala quicksort example from Wikipedia. How could the sample be disassembled step by step and what does all the syntactic sugar involved mean?
def qsort: List[Int] => List[Int] = {
case Nil => Nil
case pivot :: tail =>
val (smaller, rest) = tail.partition(_ < pivot)
qsort(smaller) ::: pivot :: qsort(rest)
}
As much as I can gather at this stage qsort is a function that takes no parameters and returns a new Function1[List[Int],List[Int]] that implements quicksort through usage of pattern matching, list manipulation and recursive calls. But I can't quite figure out where the pivot comes from, and how exactly the pattern matching syntax works in this case.
UPDATE:
Thanks everyone for the great explanations!
I just wanted to share another example of quicksort implementation which I have discovered in the Scala by Example by Martin Odersky. Although based around arrays instead of lists and less of a show-off in terms of varios Scala features I personally find it much less convoluted than its Wikipedia counterpart, and just so much more clear and to the point expression of the underlying algorithm:
def sort(xs: Array[Int]): Array[Int] = {
if (xs.length <= 1) xs
else {
val pivot = xs(xs.length / 2)
Array.concat(
sort(xs filter (pivot >)),
xs filter (pivot ==),
sort(xs filter (pivot <)))
}
}
def qsort: List[Int] => List[Int] = {
case Nil => Nil
case pivot :: tail =>
val (smaller, rest) = tail.partition(_ < pivot)
qsort(smaller) ::: pivot :: qsort(rest)
}
let's pick apart a few bits.
Naming
Operators (such as * or +) are valid candidates for method and class names in Scala (hence you can have a class called :: (or a method called :: for that matter - and indeed both exist). Scala appears to have operator-overloading but in fact it does not: it's merely that you can declare a method with the same name.
Pattern Matching
target match {
case p1 =>
case p2 =>
}
Where p1 and p2 are patterns. There are many valid patterns (you can match against Strings, types, particular instances etc). You can also match against something called an extractor. An extractor basically extracts arguments for you in the case of a match, so:
target match {
case MyExtractor(arg1, arg2, arg3) => //I can now use arg1, arg2 etc
}
In scala, if an extractor (of which a case class is an example) exists called X, then the pattern X(a, b) is equivalent to a X b. The case class :: has a constructor taking 2 arguments and putting this together we get that:
case x :: xs =>
case ::(x, xs) =>
Are equivalent. This match says "if my List is an instance of :: extract the value head into x and tail into xs". pattern-matching is also used in variable declaration. For example, if p is a pattern, this is valid:
val p = expression
This why we can declare variables like:
val x :: xs = List(1, 2, 3)
val (a, b) = xs.partition(_ % 2 == 0 ) //returns a Tuple2 which is a pattern (t1, t2)
Anonymous Functions
Secondly we have a function "literal". tail is an instance of List which has a method called partition which takes a predicate and returns two lists; one of those entries satisfying the predicate and one of those entries which did not.
val pred = (el: Int) => e < 2
Declares a function predicate which takes an Int and returns true iff the int value is less than 2. There is a shorthand for writing functions inline
tail.partition(_ < pivot) // _ is a placeholder for the parameter
tail.partition( (e: Int) => e < pivot )
These two expressions mean the same thing.
Lists
A List is a sealed abstract class with only two implementations, Nil (the empty list) and :: (also called cons), which is a non-empty list consisting of a head and a tail (which is also a list). You can now see that the pattern match is a match on whether the list is empty or not. a List can be created by cons-ing it to other lists:
val l = 1 :: 2 :: Nil
val m = List(1, 2, 3) ::: List(4, 5, 6)
The above lines are simply method calls (:: is a valid method name in scala). The only difference between these and normal method calls is that, if a method end in a colon : and is called with spaces, the order of target and parameter is reversed:
a :: b === b.::(a)
Function Types
val f: A => B
the previous line types the reference f as a function which takes an A and returns a B, so I could then do:
val a = new A
val b: B = f(a)
Hence you can see that def qsort: List[Int] => List[Int] declares a method called qsort which returns a function taking a List[Int] and returning a List[Int]. So I could obviously do:
val l = List(2, 4, 1)
val m = qsort.apply(l) //apply is to Function what run is to Runnable
val n = qsort(l) //syntactic sugar - you don't have to define apply explicitly!
Recursion
When a method call is tail recursive, Scala will optimize this into the iterator pattern. There was a msitake in my original answer because the qsort above is not tail-recursive (the tail-call is the cons operator)
def qsort: List[Int] => List[Int] = {
case Nil => Nil
case pivot :: tail =>
val (smaller, rest) = tail.partition(_ < pivot)
qsort(smaller) ::: pivot :: qsort(rest)
}
Let's rewrite that. First, replace the function literal with an instance of Function1:
def qsort: List[Int] => List[Int] = new Function1[List[Int], List[Int]] {
def apply(input: List[Int]): List[Int] = input match {
case Nil => Nil
case pivot :: tail =>
val (smaller, rest) = tail.partition(_ < pivot)
qsort(smaller) ::: pivot :: qsort(rest)
}
}
Next, I'm going to replace the pattern match with equivalent if/else statements. Note that they are equivalent, not the same. The bytecode for pattern matches are more optimized. For instance, the second if and the exception throwing below do not exist, because the compile knows the second match will always happen if the first fails.
def qsort: List[Int] => List[Int] = new Function1[List[Int], List[Int]] {
def apply(input: List[Int]): List[Int] = if (input == Nil) {
Nil
} else if (input.isInstanceOf[::[_]] &&
scala.collection.immutable.::.unapply(input.asInstanceOf[::[Int]]) != None) {
val unapplyResult = scala.collection.immutable.::.unapply(input.asInstanceOf[::[Int]]).get
val pivot = unapplyResult._1
val tail = unapplyResult._2
val (smaller, rest) = tail.partition(_ < pivot)
qsort(smaller) ::: pivot :: qsort(rest)
} else {
throw new scala.MatchError(input)
}
}
Actually, val (smaller, rest) is pattern match as well, so Let's decompose it as well:
def qsort: List[Int] => List[Int] = new Function1[List[Int], List[Int]] {
def apply(input: List[Int]): List[Int] = if (input == Nil) {
Nil
} else if (input.isInstanceOf[::[_]] &&
scala.collection.immutable.::.unapply(input.asInstanceOf[::[Int]]) != None) {
val unapplyResult0 = scala.collection.immutable.::.unapply(input.asInstanceOf[::[Int]]).get
val pivot = unapplyResult0._1
val tail = unapplyResult0._2
val tmp0 = tail.partition(_ < pivot)
if (Tuple2.unapply(tmp0) == None)
throw new scala.MatchError(tmp0)
val unapplyResult1 = Tuple2.unapply(tmp0).get
val smaller = unapplyResult1._1
val rest = unapplyResult1._2
qsort(smaller) ::: pivot :: qsort(rest)
} else {
throw new scala.MatchError(input)
}
}
Obviously, this is highly unoptmized. Even worse, there are some function calls being done more than once, which doesn't happen in the original. Unfortunately, to fix that would require some structural changes to the code.
There's still some syntactic sugar here. There is an anonymous function being passed to partition, and there is the syntactic sugar for calling functions. Rewriting those yields the following:
def qsort: List[Int] => List[Int] = new Function1[List[Int], List[Int]] {
def apply(input: List[Int]): List[Int] = if (input == Nil) {
Nil
} else if (input.isInstanceOf[::[_]] &&
scala.collection.immutable.::.unapply(input.asInstanceOf[::[Int]]) != None) {
val unapplyResult0 = scala.collection.immutable.::.unapply(input.asInstanceOf[::[Int]]).get
val pivot = unapplyResult0._1
val tail = unapplyResult0._2
val func0 = new Function1[Int, Boolean] {
def apply(input: Int): Boolean = input < pivot
}
val tmp0 = tail.partition(func0)
if (Tuple2.unapply(tmp0) == None)
throw new scala.MatchError(tmp0)
val unapplyResult1 = Tuple2.unapply(tmp0).get
val smaller = unapplyResult1._1
val rest = unapplyResult1._2
qsort.apply(smaller) ::: pivot :: qsort.apply(rest)
} else {
throw new scala.MatchError(input)
}
}
For once, the extensive explanations about each syntactic sugar and how it works are being done by others. :-) I hope this complements their answers. Just as a final note, the following two lines are equivalent:
qsort(smaller) ::: pivot :: qsort(rest)
qsort(rest).::(pivot).:::(qsort(smaller))
The pivot in this pattern matching example is the first element of the list:
scala> List(1,2,3) match {
| case x :: xs => println(x)
| case _ => println("empty")
| }
1
The pattern matching is based on extractors and the cons is not part of the language. It uses the infix syntax. You can also write
scala> List(1,2,3) match {
| case ::(x,xs) => println(x)
| case _ => println("empty")
| }
1
as well. So there is a type :: that looks like the cons operator. This type defines how it is extracted:
final case class ::[B](private var hd: B, private[scala] var tl: List[B]){ ... }
It's a case class so the extractor will be generated by the Scala compiler. Like in this example class A.
case class A(x : Int, y : Int)
A(1,2) match { case x A y => printf("%s %s", x, y)}
-> 1 2
Based on this machinary patterns matching is supported for Lists, Regexp and XML.