Commits

Dhananjay Nene committed 7e0da16

initial commit

Comments (0)

Files changed (7)

+Sequence
+========
+
+functions.scala
+collections.scala
+recursion.scala
+options.scala
+classes.scala
+patternmatch.scala
+// classes in principle are fairly similar to those in java eg.
+
+// class Person1 {
+//    val firstName: String
+//    val lastName: String
+//}
+
+//  val p = new Person1("D", "Nene") - will complain too many arguments
+//  val p = new Person1() - will complain first name, last name not defined
+//  val p = new Person1 {
+//      firstName = "D"
+//      lastName = "Nene"
+//  } - will complain error: reassignment to val
+//  In fact even standalone the compiler will complain about the above 
+//     - needs to be abstract
+
+
+class Person2 (val firstName: String,val lastName: String) { 
+    override def toString = "Person(" + firstName + "," + lastName + ")"
+    // note the lack of parenthesis after greet
+    def greet = println("Hello " + firstName)
+}
+
+val p = new Person2("D", "Nene")
+println(p)
+p.greet
+
+class Invoice1(id: String, amount: BigDecimal) {
+    override def toString = "Invoice[" + id + "] for Rs. " + amount
+}
+
+val i1 = new Invoice1("INV123", 34.56)
+println(i1)
+
+// Lets add some invoice items
+
+case class InvoiceItem (itemCode: String, quantity: BigDecimal, rate: BigDecimal){ }
+
+class Invoice(val id: String, items: List[InvoiceItem]) {
+    val amount = (BigDecimal(0) /: items) {(acc,i) => acc + i.quantity * i.rate}
+    override def toString = "Invoice[" + id + "] for Rs. " + amount
+}
+
+val i2 = new Invoice("INV124", 
+                List(InvoiceItem("threadXYZ", 11.22, 34.45),
+                     InvoiceItem("clothPQR", 157.91, 24.68)))
+
+println(i2)
+
+// Lets talk interfaces
+
+// Interfaces are implemented via traits
+// Multiple inheritance supported with traits, though class inheritance still
+// restricted to one. There must be one extends if inheriting (class or traits)
+// and multiple "with" (traits only)
+
+trait Named {
+    def name(): String
+}
+
+class Animal (name: String) extends Named { 
+    def name() = name
+    override def toString = "Animal(" + name + ")"
+}
+
+println(new Animal("butch"))
+
+trait Speaker {
+    def speak: String
+}
+
+trait Mover {
+    def move(x: Double, y: Double)
+}
+
+// Notice below :
+//   var for attributes whose values can change
+//   multiple inheritance
+//   object Dog - for all helper statics, factory methods etc.
+
+class Dog(name: String, var x: Double, var y: Double) extends Animal(name) with Speaker with Mover {
+    val speak = "Woof!"
+    def move(x: Double, y: Double) {
+        this.x = this.x + x
+        this.y = this.y + y
+    }
+    override def toString = "Dog(" + name + " at (" + x + "," + y + ")"
+}
+
+object Dog {
+    def apply(name: String, x: Int, y: Int) = {
+        new Dog(name,x,y)
+    }
+}
+
+val d = Dog("Buster", 3,5)
+println(d)
+println(d.speak)
+d.move(1,-1)
+println(d)
+

collections.scala

+// the most basic datatype is list
+val list5 = List(1,2,3,4,5)
+println(list5)
+
+// or if you want to print them one at a time
+
+list5 foreach { println(_) }
+
+// to be more explicit
+
+list5 foreach { element => println(element) }
+
+// foreach is used for side effects only, since it does not
+// return anything
+
+// alternatively
+
+for (element <- list5) {
+    println(element)
+}
+
+// what if we need a list of squares of numbers 1 thru 5?
+
+val listOfSquares = list5 map { n => n * n }
+println(listOfSquares)
+
+// How about a list of only the odd numbers out of the above?
+
+val listOfOdds = list5 filter { _ % 2 != 0 }
+val listOfEvens = list5 filterNot { _ % 2 != 0 }
+println(listOfOdds + ":" + listOfEvens)
+
+// And what if we want to add up all the values
+
+val sumOfList = list5.foldLeft(0){_ + _}
+println(sumOfList)
+
+// to be more explicit
+val sumOfList2 = list5.foldLeft(0){(accumulated,element) => accumulated + element}
+
+// or to be more concise
+val sumOfList3 = (0 /: list5){_ + _}
+
+// or if you wanted to go in the other direction (if it mattered)
+val sumOfList4 = (list5 :\ 0) { _ + _ }
+val sumOfList5 = list5.foldRight(0){(element,accumulated) => accumulated + element}
+
+// A reduce is similar to fold but does not require an initialisation
+
+val concatenatedList = list5.map {_.toString} reduceLeft{(acc,ele) => "" + acc + "," + ele}
+println(concatenatedList)
+
+// methods to extract beginning or end of list
+
+println(list5 head)
+println(list5 tail)
+println(list5 init)
+println(list5 last)
+println(list5 take 2)
+println(list5 drop 2)
+println(list5 takeWhile {_ < 3})
+println(list5 dropWhile {_ < 3})
+println(list5 partition {_ < 3})
+
+// other miscellaneous methods
+println(list5 reverse)
+println(list5 zip(list5 reverse))
+println(list5 size)
+println(list5 isEmpty)
+println(list5 slice(2,4))
+
+// Are all elements greater than zero
+println(list5 forall {_ > 0})
+// Is at least one element greater than 4
+println(list5 exists {_ > 4 })
+// What is the sum, product, min & max values of list
+println(list5 sum)
+println(list5 product)
+println(list5 min)
+println(list5 max)
+
+// get a comma separated string representation
+println(list5 mkString("MyList(",",",")"))
+
+// append to beginning of list
+println(0 :: list5)
+// append to end of list or concatenate lists
+// (try not to do this *NOT CONSTANT TIME*)
+
+println(list5 ::: List(6))
+
+// We saw map earlier. Sometimes the results of
+// such functions can return zero or more results
+// or wrap the result in a container. In such cases
+// a flat map is useful.
+
+// Lets say we want to extract all the characters
+// from a list of words. In such a case the function passed
+// to map, returns a list of chars per word
+
+val lwords = List("the","quick","brown","fox")
+val charsFirstAttempt = lwords map { _.toList }
+println(charsFirstAttempt)
+
+// Well we would like to see that list flattened. There's two ways
+
+println(charsFirstAttempt flatten) // or
+println(lwords flatMap {_.toList})
+// Lets look at a simple function declaration
+
+def add(i: Int, j: Int) = i + j
+
+// Note: 
+//  1. return type is automatically inferred
+//  2. No semicolons
+//  3. Even braces not mandatory
+//  4. return statement not required - last expression is implicitly returned
+
+
+// Functions can even be anonymous and assigned to a val/var
+
+val increment = (x: Int) => x + 1
+println(increment(5))
+
+// Note: 
+//  An anonymous function was assigned to a variable
+//  Analogous to function pointers in C
+
+// Functions can be passed to other functions
+
+def square(i: Int) = i * i
+
+def printComputed(number: Int, f: (Int)=> Int) = 
+    println("Computed Value is " + f(number))
+
+printComputed(3, square)
+
+// Functions could be passed anonymously too
+
+printComputed(4, (n: Int) => n * n)
+
+// Functions can be partially applied 
+
+val printSquareOf = printComputed(_:Int,square)
+printSquareOf(5)
+
+// Functions can be curried too
+
+def printComputed2(number: Int)(f: (Int)=> Int) = 
+    println("Computed Value is " + f(number))
+
+printComputed2(6)(square)
+
+// And if the last value is a function, its easy to
+// convert it into an anonymous code block
+
+printComputed2(7) { n =>
+    n * n
+}
+
+// Functions can have default arguments
+
+def computeTax(basicAmount: Float, taxPct:Int = 10) = {
+    basicAmount * (1 + (taxPct.toFloat/100))
+}
+
+println(computeTax(50))
+println(computeTax(75,8))
+
+// And named arguments too
+
+println(computeTax(taxPct = 15, basicAmount = 100))
+
+// You could also pass variable arguments
+
+def addAll(params: Int*) = {
+    // This is not the preferred way.
+    // especially given that we are using a mutable variabl
+    var sum = 0
+    for (param <- params) sum += param
+    sum
+}
+
+println(addAll(3,5,7,9))
+
+// Functions can be composed
+// earlier :
+//  def square(i: Int) = i * i
+//  val increment = (x: Int) => x + 1
+// What if we want to compute the square of the increment
+
+println(square(increment(3)))
+println(increment(square(3)))
+
+println(increment.andThen(square)(3))
+println(increment.compose(square)(3))
+
+// Partial functions (as opposed to total functions)
+// Sometimes functions are applicable only for a subset of 
+//   values
+// A square root function is defined only positive inputs
+// Or a division function is not defined for divisor being zero
+
+val safeSquareRoot: PartialFunction[Double, Double] = {
+    case num: Double if num > 0.0 => math.sqrt(num)
+}
+
+println(safeSquareRoot.isDefinedAt(1.69))
+println(safeSquareRoot(1.69))
+println(safeSquareRoot.isDefinedAt(-5.0))
+// The following will raise an exception before attempting to
+// compute the square root
+try {
+    println(safeSquareRoot(-5.0))
+} catch {
+    case e: Exception => println(e)
+}
+
+// The case statement we saw above is referred to as pattern matching
+// Usually you do not need to explicitly declare the PartialFunction type
+// it is inferred when the pattern match is a part of a code block
+
+// Functions are also objects, with the apply method defined as the function body
+
+object multiply extends Function2[Int,Int,Int] {
+    def apply(x: Int, y: Int) : Int = x * y
+}
+
+println(multiply(3,4))
+
+
+// Think Optional when you see Option
+
+// Optional values are the bane of every java developer
+// and result in the fondly named NPEs.
+
+// When you think of option imagine a list of 0 or 1 elements
+
+// That makes all the collection methods available on options
+
+// ** safely **
+
+// when there are no nulls, there can be no NullPointerExceptions
+
+val nonEmpty: Option[String] = Some("foo")
+val nonEmpty2: Option[String] = Some("bar")
+val empty: Option[String] = None
+
+// performing an operation only if value exists
+
+empty foreach {println _}
+nonEmpty foreach { println _ }
+
+// extract individual characters
+
+println(nonEmpty.flatten)
+println(empty.flatten)
+
+// call a function which itself returns an option
+
+// lets do a lookup from a dictionary
+
+val dict = Map[String,String]("foo" -> "Fooooo", "baz" -> "Baaaaz")
+
+// Lets first do it the non option way
+println(dict.get("foo"))
+println(dict.get("bar"))
+
+// even if the input was not an option, the output was an option
+println(nonEmpty.flatMap(dict.get(_)))
+println(nonEmpty2.flatMap(dict.get(_)))
+println(empty.flatMap(dict.get(_)))
+
+// what if we wanted to compute the length of the string?
+
+println(nonEmpty.map(_.length))
+println(empty.map(_.length))
+
+// test if the option has a value or is a none
+
+println(nonEmpty.isDefined)
+println(empty.isDefined)
+
+// note: converse is isEmpty
+
+// Other methods such as filter, exists, forall can be
+// performed safely
+
+// Convert a None into an option with a defined default value
+
+println(nonEmpty.orElse(Some("unspecified")))
+println(empty.orElse(Some("unspecified")))
+
+// Extract the value or use a default if no value exists
+println(nonEmpty.getOrElse("unspecified"))
+println(empty.getOrElse("unspecified"))
+
+// Create a list with one or zero items
+println(nonEmpty.toList)
+println(empty.toList)
+
+// There is also a way to explicitly use pattern matching
+// (we'll look at pattern matching in detail later)
+
+val matched = nonEmpty match {
+    case Some(x) => x
+    case None => "unspecified"
+}
+
+println(matched)

patternmatch.scala

+def factorial(n: Int): Int = {
+    n match {
+        case 0 => 1;
+        case _ if n > 0 => n * factorial(n-1)
+    }
+}
+
+println(factorial(5))
+
+// You can also match on case classes
+
+case class Trade(tickerSymbol: String, price: BigDecimal, quantity: BigDecimal) 
+
+def prioritise(t: Trade) = {
+    t match {
+        case Trade("AAA",_,_) => 1
+        case Trade(_,price,quantity) if (price * quantity) > 5000 => 2
+        case _ => 3 
+    }
+}
+val trade1 = Trade("AAA",123.45, 35.73)
+val trade2 = Trade("BBB",543.21, 12.79)
+val trade3 = Trade("CCC",12.21, 45.45)
+
+println(prioritise(trade1))
+println(prioritise(trade2))
+println(prioritise(trade3))
+def fibonacci(n: Int): List[Int] = {
+    def fibonacci(n: Int, l:List[Int],last: Int, secondLast: Int): List[Int] = {
+        if (n == 0) {
+            l
+        } else {
+            val sum = last + secondLast
+            fibonacci(n-1, sum :: l, sum, last)
+        } 
+    }
+    fibonacci(n-2,List(1,0),1,0) reverse
+}
+
+println(fibonacci(15))
+
+// We deliberately introduce a buggy version
+// which throws an exception when the countdown reaches 3
+
+def fibonacci2(n: Int): List[Int] = {
+    def fibonacci2(n: Int, l:List[Int],last: Int, secondLast: Int): List[Int] = {
+        if (n == 0) {
+            l
+        } else if (n == 3) {
+            throw new Exception
+        } else {
+            val sum = last + secondLast
+            fibonacci2(n-1, sum :: l, sum, last)
+        } 
+    }
+    fibonacci2(n-2,List(1,0),1,0) reverse
+}
+
+// Note the stack trace
+// You would've expected many fibonacci2 methods there
+// but will see only two. Thats tail call optimisation
+println(fibonacci2(15))
+
+// Note: JVM does not support tail call optimisation
+// Scala compiler instead detects the situation and replaces
+// it with a loop when it can
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.