[img]http://www.moilioncircle.com/images/post/manshow/004.jpg[/img]
原文:[url]http://www.moilioncircle.com/manshow/004.programming-in-Scala-2nd.html[/url]
题图:Scala之父'Martin Odersky'的书,十分精彩和经典,单词量大概4~5k,800多页,推荐进阶阅读。
[b]# 004.《Programming in Scala,2nd》干货摘要[/b]
《Programming in Scala,2nd》的天书级的干货摘要,
建议运行并细看,遇到看不懂的,可以认为知识点不扎实了。
@史荣久 / 2015-06-09 / CC-BY-SA-3.0
[b]## 英文的,值得细看[/b]
全书单词量,大概5k吧,像我CET4+Dict,能够啃下来。
技术书籍的语法和词汇都简单,看不懂也猜得出的。
不要以为看过本文,就不用看书了,每人每次看书的收获不同。
书中的编程例子特别好,尤其最后200行代码写出个excel。
还有本书叫《Programming Scala》,中间没有in。
书我没看,不知如何。只想说下,本文不是这本书的 :)
[b]## C5.Basic Types and Operations[/b]
[/code] scala
"""
HEX: 0xcafebabe
OCT: 0777
DEC: 0XCAFEBABEL, 35L, 255
Float: 1.23E4, 1.23F, 3e5f
Double: 3e5D
Char:'A', '\101', '\u0041'
Symbol:'cymbal
symbols are interned
"""
val s = 'aSymbol
s.name
"""|operators are actually just a nice syntax
|for ordinary method calls
""".stripMargin
1 + 2
(1).+(2)
-2.0
(2.0).unary_-
"""
How Scala’s == differs from Java’s
a * b yields a.*(b) , but a ::: b yields b.:::(a)
a ::: b ::: c is treated as a ::: (b ::: c) .
But a * b * c , by contrast, is treated as (a * b) * c
"""
[/code]
[b]## C6.Functional Objects[/b]
[/code] scala
"This won’t compile"
class Rational(n: Int, d: Int) {
require(d != 0)
override def toString = n +"/"+ d
def add(that: Rational): Rational =
new Rational(n * that.d + that.n * d, d * that.d)
}
"but these are ok, val "
class Rational(val n: Int, val d: Int) {
require(d != 0)
override def toString = n +"/"+ d
def add(that: Rational): Rational =
new Rational(n * that.d + that.n * d, d * that.d)
def this(n: Int) = this(n, 1) // auxiliary constructor
}
class Rational(n: Int, d: Int) {
require(d != 0)
private val g = gcd(n.abs, d.abs)
val numer = n / g
val denom = d / g
def this(n: Int) = this(n, 1)
def add(that: Rational): Rational =
new Rational(
numer * that.denom + that.numer * denom,
denom * that.denom
)
override def toString = numer +"/"+ denom
private def gcd(a: Int, b: Int): Int =
if (b == 0) a else gcd(b, a % b)
}
"""
‘$’ is reserved for identifiers generated by compiler
‘_’ have many other non-identifier uses.
avoid identifiers like to_string , __init__ , or name_
'constant' is the 1st character should be upper case,
such as XOffset
`:->` is represented internally as $colon$minus$greater
cannot write Thread.yield(), can write Thread.`yield`()
def * (m:###xN) , r * 2 <--> 2 * r
Method overloading .vs. Implicit conversions
"""
[/code]
[b]## C7.Built-in Control Structures[/b]
[/code] scala
"if, if-else expressions"
println(if (!true) "false" else "true")
def half(n:Int) = if (n % 2 == 0) n / 2 else
throw new RuntimeException("n must be even")
"while, do-while loops, not expressions"
var line = ""
while ((line = readLine()) == "") // This doesn’t work!
println("empty line")
//`line = readLine()` will always be `()`
"for expressions, ***big topic*** monad"
for (i <- 1 to 4) println("Iteration "+ i)
for (i <- 1 until 4) println("Iteration "+ i)
"filter and variable binding"
val nothing =
for {
file <- 1 to 4
if file % 2 == 0 // filter
line <- 2 to file
trimmed = line + 1 // binding
if trimmed % 2 == 0 // filter
} yield trimmed
"try-catch-finally"
def f(): Int = try { return 1 } finally { return 2 }
f() // Int = 2
def g(): Int = try { 1 } finally { 2 }
g() // Int = 1
"""
match-case : ***big topic***
Living without break and continue
@tailrec
breakable-break, use Exception
"""
[/code]
[b]## C8.Functions and Closures[/b]
[/code] scala
(1 to 5).filter((x) => x % 2 == 0)
(1 to 5).filter(x => x % 2 == 0)
(1 to 5).filter(_ % 2 == 0)
val f = _ + _ //missing parameter type
val f = (_: Int) + (_: Int)
"Partially applied functions"
def sum(a: Int, b: Int, c: Int) = a + b + c
val a = sum _
val b = sum(1, _: Int, 3)
val c = sum //missing arguments
"""
Why the trailing underscore?
Scala normally requires you to specify
function arguments that are left out explicitly
"""
(1 to 5).foreach(println _)
(1 to 5).foreach(println)
"""
The function value (the object) that’s created
at runtime from this function literal is called
a `closure`. The name arises from the act of
“closing” the function literal by “capturing”
the bindings of its free variables
"""
def echo(args: String*) =
for (arg <- args) println(arg)
echo("hello", "world!")
val arr = Array("What's", "up", "doc?")
echo(arr) // type mismatch
echo(arr: _*)
"Named arguments,Default parameter values"
def speed(distance: Float, time: Float = 1) =
distance / time
speed(time = 10, distance = 100)
speed(distance = 100, time = 10)
speed(100, 10)
speed(100)
[/code]
[b]## C9.Control Abstraction[/b]
[/code] scala
"Currying"
def plainOldSum(x: Int, y: Int) = x + y
plainOldSum(1, 2)
def curriedSum(x: Int)(y: Int) = x + y
curriedSum(1)(2)
def first(x: Int) = (y: Int) => x + y
val second = first(1)
second(2)
val onePlus = curriedSum(1) _
onePlus(2)
val twoPlus = curriedSum(2)_
twoPlus(2)
"Writing new control structures"
import java.io._
def withPrintWriter(file: File)(op: PrintWriter => Unit) {
val writer = new PrintWriter(file)
try {
op(writer)
} finally {
writer.close()
}
}
val file = new File("date.txt")
withPrintWriter(file) {
writer => writer.println(new java.util.Date)
}
"By-name parameters"
var assertionsEnabled = false
def myAssert(predicate: () => Boolean) =
if (assertionsEnabled && !predicate())
throw new AssertionError
myAssert(() => 5 > 3)
myAssert(5 > 3) // Won’t work, because missing () =>
def byNameAssert(predicate: => Boolean) =
if (assertionsEnabled && !predicate)
throw new AssertionError
byNameAssert(5 > 3)
def boolAssert(predicate: Boolean) =
if (assertionsEnabled && !predicate)
throw new AssertionError
val x = 0
boolAssert(10 / x == 0) // / by zero
byNameAssert(10 / x == 0) // OK
[/code]
[b]## C10.Composition and Inheritance[/b]
[/code] scala
"""
The recommended convention is to use a
parameterless method whenever there are
no parameters and the method accesses
mutable state only by reading fields
of the containing object (in particular,
it does not change mutable state)
"""
"""
Scala has just two namespaces for definitions
in place of Java’s four (fields, methods, types,
and packages) Scala’s two namespaces are:
• values (fields, methods, packages,
singleton objects)
• types (class and trait names)
"""
:paste
object Element {
private class ArrayElement(
val contents: Array[String]) extends Element
private class LineElement(s: String) extends Element {
val contents = Array(s)
override def width = s.length
override def height = 1
}
private class UniformElement(
ch: Char,
override val width: Int,
override val height: Int) extends Element {
private val line = ch.toString * width
def contents = Array.fill(height)(line)
}
def apply(contents: Array[String]): Element =
new ArrayElement(contents)
def apply(chr: Char, width: Int, height: Int): Element =
new UniformElement(chr, width, height)
def apply(line: String): Element =
new LineElement(line)
}
abstract class Element {
def contents: Array[String]
def width: Int = contents(0).length
def height: Int = contents.length
def above(that: Element): Element = {
val this1 = this widen that.width
val that1 = that widen this.width
Element(this1.contents ++ that1.contents)
}
def beside(that: Element): Element = {
val this1 = this heighten that.height
val that1 = that heighten this.height
Element(
for ((line1, line2) <- this1.contents zip that1.contents)
yield line1 + line2)
}
def widen(w: Int): Element =
if (w <= width) this
else {
val left = Element(' ', (w - width) / 2, height)
var right = Element(' ', w - width - left.width, height)
left beside this beside right
}
def heighten(h: Int): Element =
if (h <= height) this
else {
val top = Element(' ', width, (h - height) / 2)
var bot = Element(' ', width, h - height - top.height)
top above this above bot
}
override def toString = contents mkString "\n"
}
val space = Element(" ")
val corner = Element("+")
def spiral(nEdges: Int, direction: Int): Element = {
if (nEdges == 1)
Element("+")
else {
val sp = spiral(nEdges - 1, (direction + 3) % 4)
def verticalBar = Element('|', 1, sp.height)
def horizontalBar = Element('-', sp.width, 1)
if (direction == 0)
(corner beside horizontalBar) above (sp beside space)
else if (direction == 1)
(sp above space) beside (corner above verticalBar)
else if (direction == 2)
(space beside sp) above (horizontalBar beside corner)
else
(verticalBar above corner) beside (space above sp)
}
}
//end :paste ctrl-d
spiral(17,0)
[/code]
[b]## C11.Scala’s Hierarchy[/b]
[/code] scala
"""
|<-AnyVal<-(Unit,Int,Double...)<-Nothing
Any | v
|<-AnyRef<-(Seq,Set,Map,String...)<-Null
"""
[b]## C12.Traits[/b]
[/code] scala
"mix-in traits rather than inherit from them"
class Point(x: Int, y: Int)
trait NoPoint(x: Int, y: Int) // Does not compile
trait NoPoint(val x: Int, val y: Int)
class Animal
trait Furry extends Animal
trait HasLegs extends Animal
trait FourLegged extends HasLegs
class Cat extends Animal with Furry with FourLegged
"""
Animal:Animal,AnyRef,Any
Furry:Furry,Animal,AnyRef,Any
FourLegged:FourLegged,HasLegs,Animal,AnyRef,Any
HasLegs:HasLegs,Animal,AnyRef,Any
Cat:Cat,FourLegged,HasLegs,Furry,Animal,AnyRef,Any
When any of these classes and traits invokes a method via `super` , the implementation invoked will be
Cat->FourLegged->HasLegs->Furry->Animal->AnyRef->Any
If the behavior will not be reused,
then make it a concrete class.
If it might be reused in multiple, unrelated classes,
make it a trait. Only traits can be mixed into
different parts of the class hierarchy.
If you want to inherit from it in Java code,
use an abstract class.
"""
[/code]
[b]## C13.Packages and Imports[/b]
[/code] scala
"follow Java’s reverse-domain-name convention"
package com {
package moilioncircle {
package public {
class P01
}
class Readme {
val booster1 = 0
}
}
package trydofor {
class X3
}
}
import com.trydofor.X3
import com.moilioncircle._ // *,all
import com.moilioncircle{Readme => R, public} // rename
import com.moilioncircle{Readme => R, _} // rename, *
import com.{trydofor => _, _} // all but trydofor
def regex(){
import java.util.regex
}
"""
private[bobsrockets] access within outer package
private[navigation] same as package visibility in Java
private[Navigator] same as private in Java
private[LegOfJourney] same as private in Scala
private[this] access only from same object
"""
package bobsrockets
package navigation {
private[bobsrockets] class Navigator {
protected[navigation] def useStarChart() {}
class LegOfJourney {
private[Navigator] val distance = 100
}
private[this] var speed = 200
}
}
"""
A class shares all its access rights
with its companion object and vice versa.
"""
"""
package object. Each package is allowed
to have one package object. Any definitions
placed in a package object are
considered members of the package itself.
"""
// In file bobsdelights/package.scala
package object bobsdelights {
def showFruit(fruit: Fruit) {
import fruit._
println(name +"s are "+ color)
}
}
import bobsdelights.showFruit
[/code]
[b]## C14.Assertions and Unit Testing[/b]
[b]## C15.Case Classes and Pattern Matching[/b]
[b]## C16.Working with Lists[/b]
[b]## C17.Collections[/b]
[b]## C18.Stateful Objects[/b]
[b]## C19.Type Parameterization[/b]
[b]## C20.Abstract Members[/b]
[b]## C21.Implicit Conversions and Parameters[/b]
[b]## C22.Implementing Lists[/b]
[b]## C23.For Expressions Revisited[/b]
[b]## C24.The Scala Collections API[/b]
[b]## C25.The Architecture of Scala Collections[/b]
[b]## C26.Extractors[/b]
[b]## C27.Annotations[/b]
[b]## C28.Working with XML[/b]
[b]## C29.Modular Programming Using Objects[/b]
[b]## C30.Object Equality[/b]
[b]## C32.Actors and Concurrency[/b]
[b]## C33.Combinator Parsing[/b]
[b]## C34.GUI Programming[/b]
[b]## C35.The SCells Spreadsheet[/b]
原文:[url]http://www.moilioncircle.com/manshow/004.programming-in-Scala-2nd.html[/url]
题图:Scala之父'Martin Odersky'的书,十分精彩和经典,单词量大概4~5k,800多页,推荐进阶阅读。
[b]# 004.《Programming in Scala,2nd》干货摘要[/b]
《Programming in Scala,2nd》的天书级的干货摘要,
建议运行并细看,遇到看不懂的,可以认为知识点不扎实了。
@史荣久 / 2015-06-09 / CC-BY-SA-3.0
[b]## 英文的,值得细看[/b]
全书单词量,大概5k吧,像我CET4+Dict,能够啃下来。
技术书籍的语法和词汇都简单,看不懂也猜得出的。
不要以为看过本文,就不用看书了,每人每次看书的收获不同。
书中的编程例子特别好,尤其最后200行代码写出个excel。
还有本书叫《Programming Scala》,中间没有in。
书我没看,不知如何。只想说下,本文不是这本书的 :)
# pdf变成text
pdftotext Programming-in-Scala-2nd.pdf w.txt
# 变小写,只字母,去重复,统计
cat p.txt|tr 'A-Z \t' 'a-z\n'|
grep -E '^[A-Za-z]+$'|sort|uniq|wc -l
# 6180
# 去掉ing,ed和同根词,乐观估计单词量在4~5k
[b]## C5.Basic Types and Operations[/b]
[/code] scala
"""
HEX: 0xcafebabe
OCT: 0777
DEC: 0XCAFEBABEL, 35L, 255
Float: 1.23E4, 1.23F, 3e5f
Double: 3e5D
Char:'A', '\101', '\u0041'
Symbol:'cymbal
symbols are interned
"""
val s = 'aSymbol
s.name
"""|operators are actually just a nice syntax
|for ordinary method calls
""".stripMargin
1 + 2
(1).+(2)
-2.0
(2.0).unary_-
"""
How Scala’s == differs from Java’s
a * b yields a.*(b) , but a ::: b yields b.:::(a)
a ::: b ::: c is treated as a ::: (b ::: c) .
But a * b * c , by contrast, is treated as (a * b) * c
"""
[/code]
[b]## C6.Functional Objects[/b]
[/code] scala
"This won’t compile"
class Rational(n: Int, d: Int) {
require(d != 0)
override def toString = n +"/"+ d
def add(that: Rational): Rational =
new Rational(n * that.d + that.n * d, d * that.d)
}
"but these are ok, val "
class Rational(val n: Int, val d: Int) {
require(d != 0)
override def toString = n +"/"+ d
def add(that: Rational): Rational =
new Rational(n * that.d + that.n * d, d * that.d)
def this(n: Int) = this(n, 1) // auxiliary constructor
}
class Rational(n: Int, d: Int) {
require(d != 0)
private val g = gcd(n.abs, d.abs)
val numer = n / g
val denom = d / g
def this(n: Int) = this(n, 1)
def add(that: Rational): Rational =
new Rational(
numer * that.denom + that.numer * denom,
denom * that.denom
)
override def toString = numer +"/"+ denom
private def gcd(a: Int, b: Int): Int =
if (b == 0) a else gcd(b, a % b)
}
"""
‘$’ is reserved for identifiers generated by compiler
‘_’ have many other non-identifier uses.
avoid identifiers like to_string , __init__ , or name_
'constant' is the 1st character should be upper case,
such as XOffset
`:->` is represented internally as $colon$minus$greater
cannot write Thread.yield(), can write Thread.`yield`()
def * (m:###xN) , r * 2 <--> 2 * r
Method overloading .vs. Implicit conversions
"""
[/code]
[b]## C7.Built-in Control Structures[/b]
[/code] scala
"if, if-else expressions"
println(if (!true) "false" else "true")
def half(n:Int) = if (n % 2 == 0) n / 2 else
throw new RuntimeException("n must be even")
"while, do-while loops, not expressions"
var line = ""
while ((line = readLine()) == "") // This doesn’t work!
println("empty line")
//`line = readLine()` will always be `()`
"for expressions, ***big topic*** monad"
for (i <- 1 to 4) println("Iteration "+ i)
for (i <- 1 until 4) println("Iteration "+ i)
"filter and variable binding"
val nothing =
for {
file <- 1 to 4
if file % 2 == 0 // filter
line <- 2 to file
trimmed = line + 1 // binding
if trimmed % 2 == 0 // filter
} yield trimmed
"try-catch-finally"
def f(): Int = try { return 1 } finally { return 2 }
f() // Int = 2
def g(): Int = try { 1 } finally { 2 }
g() // Int = 1
"""
match-case : ***big topic***
Living without break and continue
@tailrec
breakable-break, use Exception
"""
[/code]
[b]## C8.Functions and Closures[/b]
[/code] scala
(1 to 5).filter((x) => x % 2 == 0)
(1 to 5).filter(x => x % 2 == 0)
(1 to 5).filter(_ % 2 == 0)
val f = _ + _ //missing parameter type
val f = (_: Int) + (_: Int)
"Partially applied functions"
def sum(a: Int, b: Int, c: Int) = a + b + c
val a = sum _
val b = sum(1, _: Int, 3)
val c = sum //missing arguments
"""
Why the trailing underscore?
Scala normally requires you to specify
function arguments that are left out explicitly
"""
(1 to 5).foreach(println _)
(1 to 5).foreach(println)
"""
The function value (the object) that’s created
at runtime from this function literal is called
a `closure`. The name arises from the act of
“closing” the function literal by “capturing”
the bindings of its free variables
"""
def echo(args: String*) =
for (arg <- args) println(arg)
echo("hello", "world!")
val arr = Array("What's", "up", "doc?")
echo(arr) // type mismatch
echo(arr: _*)
"Named arguments,Default parameter values"
def speed(distance: Float, time: Float = 1) =
distance / time
speed(time = 10, distance = 100)
speed(distance = 100, time = 10)
speed(100, 10)
speed(100)
[/code]
[b]## C9.Control Abstraction[/b]
[/code] scala
"Currying"
def plainOldSum(x: Int, y: Int) = x + y
plainOldSum(1, 2)
def curriedSum(x: Int)(y: Int) = x + y
curriedSum(1)(2)
def first(x: Int) = (y: Int) => x + y
val second = first(1)
second(2)
val onePlus = curriedSum(1) _
onePlus(2)
val twoPlus = curriedSum(2)_
twoPlus(2)
"Writing new control structures"
import java.io._
def withPrintWriter(file: File)(op: PrintWriter => Unit) {
val writer = new PrintWriter(file)
try {
op(writer)
} finally {
writer.close()
}
}
val file = new File("date.txt")
withPrintWriter(file) {
writer => writer.println(new java.util.Date)
}
"By-name parameters"
var assertionsEnabled = false
def myAssert(predicate: () => Boolean) =
if (assertionsEnabled && !predicate())
throw new AssertionError
myAssert(() => 5 > 3)
myAssert(5 > 3) // Won’t work, because missing () =>
def byNameAssert(predicate: => Boolean) =
if (assertionsEnabled && !predicate)
throw new AssertionError
byNameAssert(5 > 3)
def boolAssert(predicate: Boolean) =
if (assertionsEnabled && !predicate)
throw new AssertionError
val x = 0
boolAssert(10 / x == 0) // / by zero
byNameAssert(10 / x == 0) // OK
[/code]
[b]## C10.Composition and Inheritance[/b]
[/code] scala
"""
The recommended convention is to use a
parameterless method whenever there are
no parameters and the method accesses
mutable state only by reading fields
of the containing object (in particular,
it does not change mutable state)
"""
"""
Scala has just two namespaces for definitions
in place of Java’s four (fields, methods, types,
and packages) Scala’s two namespaces are:
• values (fields, methods, packages,
singleton objects)
• types (class and trait names)
"""
:paste
object Element {
private class ArrayElement(
val contents: Array[String]) extends Element
private class LineElement(s: String) extends Element {
val contents = Array(s)
override def width = s.length
override def height = 1
}
private class UniformElement(
ch: Char,
override val width: Int,
override val height: Int) extends Element {
private val line = ch.toString * width
def contents = Array.fill(height)(line)
}
def apply(contents: Array[String]): Element =
new ArrayElement(contents)
def apply(chr: Char, width: Int, height: Int): Element =
new UniformElement(chr, width, height)
def apply(line: String): Element =
new LineElement(line)
}
abstract class Element {
def contents: Array[String]
def width: Int = contents(0).length
def height: Int = contents.length
def above(that: Element): Element = {
val this1 = this widen that.width
val that1 = that widen this.width
Element(this1.contents ++ that1.contents)
}
def beside(that: Element): Element = {
val this1 = this heighten that.height
val that1 = that heighten this.height
Element(
for ((line1, line2) <- this1.contents zip that1.contents)
yield line1 + line2)
}
def widen(w: Int): Element =
if (w <= width) this
else {
val left = Element(' ', (w - width) / 2, height)
var right = Element(' ', w - width - left.width, height)
left beside this beside right
}
def heighten(h: Int): Element =
if (h <= height) this
else {
val top = Element(' ', width, (h - height) / 2)
var bot = Element(' ', width, h - height - top.height)
top above this above bot
}
override def toString = contents mkString "\n"
}
val space = Element(" ")
val corner = Element("+")
def spiral(nEdges: Int, direction: Int): Element = {
if (nEdges == 1)
Element("+")
else {
val sp = spiral(nEdges - 1, (direction + 3) % 4)
def verticalBar = Element('|', 1, sp.height)
def horizontalBar = Element('-', sp.width, 1)
if (direction == 0)
(corner beside horizontalBar) above (sp beside space)
else if (direction == 1)
(sp above space) beside (corner above verticalBar)
else if (direction == 2)
(space beside sp) above (horizontalBar beside corner)
else
(verticalBar above corner) beside (space above sp)
}
}
//end :paste ctrl-d
spiral(17,0)
[/code]
[b]## C11.Scala’s Hierarchy[/b]
[/code] scala
"""
|<-AnyVal<-(Unit,Int,Double...)<-Nothing
Any | v
|<-AnyRef<-(Seq,Set,Map,String...)<-Null
"""
[b]## C12.Traits[/b]
[/code] scala
"mix-in traits rather than inherit from them"
class Point(x: Int, y: Int)
trait NoPoint(x: Int, y: Int) // Does not compile
trait NoPoint(val x: Int, val y: Int)
class Animal
trait Furry extends Animal
trait HasLegs extends Animal
trait FourLegged extends HasLegs
class Cat extends Animal with Furry with FourLegged
"""
Animal:Animal,AnyRef,Any
Furry:Furry,Animal,AnyRef,Any
FourLegged:FourLegged,HasLegs,Animal,AnyRef,Any
HasLegs:HasLegs,Animal,AnyRef,Any
Cat:Cat,FourLegged,HasLegs,Furry,Animal,AnyRef,Any
When any of these classes and traits invokes a method via `super` , the implementation invoked will be
Cat->FourLegged->HasLegs->Furry->Animal->AnyRef->Any
If the behavior will not be reused,
then make it a concrete class.
If it might be reused in multiple, unrelated classes,
make it a trait. Only traits can be mixed into
different parts of the class hierarchy.
If you want to inherit from it in Java code,
use an abstract class.
"""
[/code]
[b]## C13.Packages and Imports[/b]
[/code] scala
"follow Java’s reverse-domain-name convention"
package com {
package moilioncircle {
package public {
class P01
}
class Readme {
val booster1 = 0
}
}
package trydofor {
class X3
}
}
import com.trydofor.X3
import com.moilioncircle._ // *,all
import com.moilioncircle{Readme => R, public} // rename
import com.moilioncircle{Readme => R, _} // rename, *
import com.{trydofor => _, _} // all but trydofor
def regex(){
import java.util.regex
}
"""
private[bobsrockets] access within outer package
private[navigation] same as package visibility in Java
private[Navigator] same as private in Java
private[LegOfJourney] same as private in Scala
private[this] access only from same object
"""
package bobsrockets
package navigation {
private[bobsrockets] class Navigator {
protected[navigation] def useStarChart() {}
class LegOfJourney {
private[Navigator] val distance = 100
}
private[this] var speed = 200
}
}
"""
A class shares all its access rights
with its companion object and vice versa.
"""
"""
package object. Each package is allowed
to have one package object. Any definitions
placed in a package object are
considered members of the package itself.
"""
// In file bobsdelights/package.scala
package object bobsdelights {
def showFruit(fruit: Fruit) {
import fruit._
println(name +"s are "+ color)
}
}
import bobsdelights.showFruit
[/code]
[b]## C14.Assertions and Unit Testing[/b]
"""
Assertions (and ensuring checks) can be enabled
and disabled using the JVM’s -ea and -da flags
"""
def addNaturals(nats: List[Int]): Int = {
assert(nats != null)
require(nats forall (_ >= 0), "negative numbers")
nats.foldLeft(0)(_ + _)
} ensuring(_ >= 0)
"""
Using JUnit and TestNG ==>XUnit
Tests as specifications==>ScalaTest
Property-based testing ==>ScalaCheck
"""
[b]## C15.Case Classes and Pattern Matching[/b]
//case class BinOp,Number
"Wildcard patterns"
expr match {
case BinOp(_, _, _) => println(expr +" is BinOp")
case _ => println("It's something else")
}
"Constant patterns"
def describe(x: Any) = x match {
case 5 => "five"
case true => "truth"
case "hello" => "hi!"
case Nil => "the empty list"
case _ => "something else"
}
"Variable patterns"
expr match {
case 0 => "zero"
case somethingElse => "not zero: "+ somethingElse
}
"Variable or constant?"
import math.{E, Pi}
E match {
case Pi => "strange math? Pi = "+ Pi
case _ => "OK"
}
val pi = math.Pi
E match {
case pi => "strange math? Pi = "+ pi
}
E match {
case `pi` => "strange math? Pi = "+ pi
case _ => "OK"
}
// as a constant, not as a variable:
"Constructor patterns"
expr match {
case BinOp("+", e, Number(0)) =>
println("a deep match")
case _ =>
}
"Sequence patterns"
expr match {
case List(0, _, _) => println("found it")
case _ =>
}
expr match {
case List(0, _*) => println("found it")
case _ =>
}
"Tuple patterns"
expr match {
case (a, b, c) => println("matched "+ a + b + c)
case _ =>
}
"Typed patterns"
x match {
case s: String => s.length
case m: Map[_, _] => m.size // Type erasure
case _ => -1
}
x match {
case a: Array[String] => "yes" // NOT Type erasure
case _ => "no"
}
"Variable binding"
//UnOp("abs", _)
expr match {
case UnOp("abs", e @ UnOp("abs", _)) => e
case _ =>
}
"Pattern guards"
e match {
case BinOp("+", x, y) if x == y =>
BinOp("*", x, Number(2))
case _ => e
}
"Sealed classes"
sealed abstract class Expr
case class Var(name: String) extends Expr
case class Number(num: Double) extends Expr
"The Option type"
x match {
case Some(s) => s
case None => "?"
}
"Patterns everywhere"
val myTuple = (123, "abc")
val (number, string) = myTuple
val BinOp(op, left, right) = exp
val second: List[Int] => Int = {
case x :: y :: _ => y
}
val a :: b :: rest = fruit
val List(a, b, c) = fruit
for ((country, city) <- capitals)
for (Some(fruit) <- results)
[b]## C16.Working with Lists[/b]
"The list type in Scala is covariant"
val fruit = List("apples", "oranges", "pears")
val fruit = "apples" :: ("oranges" :: ("pears" :: Nil))
"""
xs ::: ys ::: zs
is interpreted like this:
xs ::: (ys ::: zs)
"""
val abcde = List('a', 'b', 'c', 'd', 'e')
abcde.last //e
abcde.init // List(a, b, c, d)
abcde.reverse // List(e, d, c, b, a)
abcde take 2 // List(a, b)
abcde drop 2 // List(c, d, e)
abcde splitAt 2 // (List(a, b),List(c, d, e))
abcde(2) // Char=c
abcde.indices //Range(0, 1, 2, 3, 4)
List(List(1, 2), List(3), List(), List(4, 5)).flatten
// List(1, 2, 3, 4, 5)
abcde.indices zip abcde
// IndexedSeq((0,a), (1,b), (2,c), (3,d), (4,e))
val zipped = abcde zip List(1, 2, 3)
// List((a,1), (b,2), (c,3))
abcde.zipWithIndex
// List((a,0), (b,1), (c,2), (d,3),(e,4))
zipped.unzip
// (List(a, b, c),List(1, 2, 3))
abcde mkString ("[", ",", "]")
// String = [a,b,c,d,e]
val buf = new StringBuilder
abcde addString (buf, "(", ";", ")")
// StringBuilder = (a;b;c;d;e)
val arr = abcde.toArray // Array(a, b, c, d, e)
arr.toList // List(a, b, c, d, e)
val arr2 = new Array[Int](10)
//Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
List(1, 2, 3) copyToArray (arr2, 3)
//Array(0, 0, 0, 1, 2, 3, 0, 0, 0, 0)
"Higher-order methods on class List"
List(1, 2, 3) map (_ + 1) // List(2, 3, 4)
val words = List("the", "quick")
words map (_.toList)
//List(List(t, h, e), List(q, u, i, c, k))
words flatMap (_.toList)
//List(t, h, e, q, u, i, c, k)
for (i <- List.range(1, 5);
j <- List.range(1, i)) yield (i, j)
List(1, 2, 3, 4, 5) foreach (println)
List(1, 2, 3, 4, 5) filter (_ % 2 == 0) //List(2, 4)
List(1, 2, 3, 4, 5) partition (_ % 2 == 0)
// (List(2, 4),List(1, 3, 5))
List(1, 2, 3, 4, 5) find (_ % 2 == 0) // Some(2)
List(1, 2, 3, 4, 5) find (_ <= 0) // None
List(1, 2, 3, -4, 5) takeWhile (_ > 0)
//List(1, 2, 3)
List(1, 2, 3, -4, 5) dropWhile (_ > 0)
//List(-4, 5)
List(1, 2, 3, -4, 5) span (_ > 0)
//(List(1, 2, 3),List(-4, 5))
List(1, 2, 3, -4, 5) forall (_>0) //false
List(1, 2, 3, -4, 5) exists (_>0) //true
("0" /: List(1, 2, 3, 4, 5))(_+","+_)
List(1, 2, 3, 4, 5)./:("0")(_+","+_)
//String = 0,1,2,3,4,5
(List(1, 2, 3, 4, 5) :\ "6")(_+","+_)
//String = 1,2,3,4,5,6
List(1, -3, 4, 2, 6) sortWith (_ < _)
//List(-3, 1, 2, 4, 6)
List.fill(5)('a') // List(a, a, a, a, a)
List.fill(2, 3)('b')
// List(List(b, b, b), List(b, b, b))
List.tabulate(5)(n => n * n)
//List(0, 1, 4, 9, 16)
List.tabulate(2,3)(_ * _)
//List(List(0, 0, 0), List(0, 1, 2))
[b]## C17.Collections[/b]
"Sequences"
List("red", "blue", "green")
Array(5, 4, 3, 2, 1)
new Array[Int](5)
import scala.collection.mutable.ListBuffer
import scala.collection.mutable.ArrayBuffer
// Strings (via StringOps )
Set(1,2,3,4,5)
Map("i" -> 1, "ii" -> 2)
"factory method will return a different class"
scala.collection.immutable.{EmptySet,
Set1,Set2,Set3,Set4,HashSet}
scala.collection.immutable.{EmptyMap,
Map1,Map2,Map3,Map4,HashMap}
"Sorted sets and maps"
import scala.collection.immutable.TreeSet
import scala.collection.immutable.TreeMap
"mutable versus immutable collections"
import scala.collection.immutable
import scala.collection.mutable
"Converting between mutable and immutable"
val set = Set(1,2,3)
val mutaSet = mutable.Set.empty ++= set
val immutaSet = Set.empty ++ mutaSet
val map = Map("i" -> 1, "ii" -> 2)
val mutaMap = mutable.Map.empty ++= map
val immutaMap = Map.empty ++ mutaMap
"""
Because tuples can combine objects of different
types, tuples do not inherit from Traversable
"""
[b]## C18.Stateful Objects[/b]
class Time {
private[this] var h = 12
def hour: Int = h
def hour_= (x: Int) {
require(0 <= x && x < 24)
h = x
}
}
[b]## C19.Type Parameterization[/b]
"Private constructors and factory methods"
class Queue[T] private ( // private constructor
private val leading: List[T], // private leading()
val trailing: List[T] // public trailing()
)
:paste
class Queue[T] private (
private val leading: List[T],
private val trailing: List[T]
)
object Queue {
// constructs a queue with initial elements ‘xs’
def apply[T](xs: T*) = new Queue[T](xs.toList, Nil)
}
// :paste ctlr-d
:paste
trait Queue[T] {
def head: T
def tail: Queue[T]
}
object Queue {
def apply[T](xs: T*): Queue[T] =
new QueueImpl[T](xs.toList, Nil)
private class QueueImpl[T](
private val leading: List[T],
private val trailing: List[T]
) extends Queue[T]{
def head: T = leading.head
def tail: QueueImpl[T] =
new QueueImpl(leading.tail, trailing)
}
}
// :paste ctlr-d
"require less and provide more"
trait Function1[-S, +T] {
def apply(x: S): T
}
class Queue[T](
private[this] var leading: List[T] //Object private data
){
def lower[U >: T](x: U){} //Lower bounds
def upper[V <: T](x: V){} //Upper bounds
}
[b]## C20.Abstract Members[/b]
trait Abstract {
type T //abstract type
def transform(x: T): T //abstract method
val initial: T //abstract val
var current: T //abstract var
}
class Concrete extends Abstract {
type T = String
def transform(x: String) = x + x
val initial = "hi"
var current = initial
}
abstract class Fruit {
val v: String // ‘v’ for value
def m: String // ‘m’ for method
}
abstract class BadApple extends Fruit {
def v: String // ERROR: cannot override a ‘val’ with a ‘def’
val m: String // OK to override a ‘def’ with a ‘val’
}
trait AbstractTime {
var hour: Int
}
trait AbstractTime {
def hour: Int // getter for ‘hour’
def hour_=(x: Int) // setter for ‘hour’
}
"""
A class parameter argument is evaluated
before it is passed to the class constructor
(unless the parameter is by-name)
"""
trait RationalTrait {
val numerArg: Int
val denomArg: Int
require(denomArg != 0)
override def toString = numerArg +"/"+ denomArg
}
new RationalTrait {
val numerArg = 1
val denomArg = 2
} // IllegalArgumentException: requirement failed
"Pre-initialized fields"
val x = 1
new { //anonymous
val numerArg = 1 * x
val denomArg = 2 * x
} with RationalTrait
object twoThirds extends { // objects
val numerArg = 2
val denomArg = 3
} with RationalTrait
class RationalClass(n: Int, d: Int) extends { //named
val numerArg = n
val denomArg = d
} with RationalTrait
""
class Food
class Grass extends Food
abstract class Animal {
def eat(food: Food)
}
class Cow extends Animal {
override def eat(food: Grass) {} // This won’t compile, Food
} // Cow can eat any Food ?
// compiled and type checking
abstract class Animal {
type SuitableFood <: Food
def eat(food: SuitableFood)
}
class Cow extends Animal {
type SuitableFood = Grass
override def eat(food: Grass) {} // OK
}
"Structural subtyping"
// animal that eats grass
var animals: List[Animal { type SuitableFood = Grass }]=Nil
def using[T <: { def close(): Unit }, S](obj: T){}
"Enumeration"
object Color extends Enumeration {
val Red, Green = Value
val Blue = Value("My Blue")
}
"Currency Example is very Good !!"
[b]## C21.Implicit Conversions and Parameters[/b]
"Marking Rule: Only definitions marked implicit are available."
implicit def intToString(x: Int) = x.toString
"""
Scope Rule: An inserted implicit conversion must be in scope
as a single identifier, or be associated with the source or
target type of the conversion.
"""
:paste
object Count {
implicit def Int2Count(x: Int): Count = new Count
}
class Count { }
import Count._
// :paste ctlr-d
"""
One-at-a-time Rule: Only one implicit is tried.
Explicits-First Rule: Whenever code type checks as
it is written, no implicits are attempted.
The compiler will not change code that already works.
Where implicits are tried.
There are three places implicits are used:
1 conversions to an expected type,
2 conversions of the receiver of a selection,
3 and implicit parameters.
"""
"conversions to an expected type,"
val i: Int = 3.5 //type mismatch;
implicit def doubleToInt(x: Double) = x.toInt
val i: Int = 3.5
val i: Int = doubleToInt(3.5)
"Converting the receiver"
class Rational(n: Int, d: Int) {
def + (that: Int): Rational = new Rational(n+d*that,d)
override def toString = n +"/"+ d
}
val oneHalf = new Rational(1, 2)
oneHalf + 1
1 + oneHalf // not good
implicit def intToRational(x: Int) = new Rational(x, 1)
// how about `def + (that :XXX)` * n
"Simulating new syntax --->"
object UM {
class M[A](x: A) {
def ---> [B](y: B): Tuple2[A, B] = Tuple2(x, y)
}
implicit def any2M[A](x: A): M[A] = new M(x)
}
import UM._
Map(1 ---> "one", 2 ---> "two")
"""View bounds
You can think of “ T < % Ordered[T] ” as saying,
“I can use any T, so long as T can be treated as
an Ordered[T].” implicit T => Ordered[T].
"""
"Debugging implicits `-Xprint:typer`"
[b]## C22.Implementing Lists[/b]
"List in java is interface, but in scala is class"
sealed abstract class List[+A]
case object Nil extends List[Nothing]
final case class ::[T](hd: T, tl: List[T]) extends List[T]
"""
Class `::` , pronounced “cons” for “construct,”
represents non-empty lists. It’s named that way
in order to support pattern matching.
x :: xs is treated as ::(x, xs)
"""
"""
The List class in practice
suffer from the same stack overflow problem as
the non-tail recursive implementation of incAll.
Therefore, most methods in the real implementation
of class List avoid recursion and use loops
with list buffers instead
The design of Scala’s List and ListBuffer
is quite similar to what’s done in Java’s
pair of classes String and StringBuffer
"""
[b]## C23.For Expressions Revisited[/b]
case class Person(name: String,isMale: Boolean,children: Person*)
val lara = Person("Lara", false)
val bob = Person("Bob", true)
val julie = Person("Julie", false, lara, bob)
val persons = List(lara, bob, julie)
"""
using a withFilter call instead of filter would
avoid the creation of an intermediate data structure
"""
persons filter (p => !p.isMale) flatMap (p =>
(p.children map (c => (p.name, c.name))))
persons withFilter (p => !p.isMale) flatMap (p =>
(p.children map (c => (p.name, c.name))))
"braces instead of parentheses"
for (p <- persons; if !p.isMale; c <- p.children)
yield (p.name, c.name)
for {
p <- persons //generator
m = p.isMale //definition same as `val m = p.isMale`
if !m //filter
c <- p.children //generator
}yield (p.name, c.name)
"""
for ( x <- expr1 ) yield expr2
==> expr1.map( x => expr2 )
for( x <- expr1 if expr2 ) yield expr3
==> for(x <- expr1 withFilter (x => expr2 )) yield expr3
==> expr1 withFilter ( x => expr2 ) map ( x => expr3 )
for ( x <- expr1 ; y <- expr2 ; seq) yield expr3
==> expr1.flatMap( x => for ( y <- expr2 ; seq) yield expr3 )
==> ...
for ( x <- expr1 ; y = expr2 ; seq) yield expr3
==> for((x, y) <- for(x <- expr1) yield (x, expr2); seq)
yield expr3
for ( x <- expr1 ) body // without yield
==> expr1 foreach ( x => body)
"""
"""
If your type defines just map , it allows for expressions
consisting of a single generator.
If it defines flatMap as well as map , it allows for
expressions consisting of several generators.
If it defines foreach , it allows for loops
(both with single and multiple generators).
If it defines withFilter , it allows for filter expressions
starting with an if in the for expression.
"""
abstract class C[A] {
def map[B](f: A => B): C[B]
def flatMap[B](f: A => C[B]): C[B]
def withFilter(p: A => Boolean): C[A]
def foreach(b: A => Unit): Unit
}
[b]## C24.The Scala Collections API[/b]
"""
Traversable
Iterable
Seq
IndexedSeq
Vector
ResizableArray
GenericArray
LinearSeq
MutableList
List
Stream
Buffer
ListBuffer
ArrayBuffer
Set
SortedSet
TreeSet
HashSet (mutable)
LinkedHashSet
HashSet (immutable)
BitSet
EmptySet, Set1, Set2, Set3, Set4
Map
SortedMap
TreeMap
HashMap (mutable)
LinkedHashMap (mutable)
HashMap (immutable)
EmptyMap, Map1, Map2, Map3, Map4
"""
"""
If xs is some collection, then xs.view is the same
collection, but with all transformers implemented lazily.
To get back from a view to a strict collection,
you can use the force method.
"""
val v = Vector(1 to 3) // Vector(Range(1, 2, 3))
val v = Vector(1 to 3: _*) // Vector(1, 2, 3)
val vi = v map (_ + 1) map (_ * 2) // Vector(4, 6, 8)
val vl = v.view map (_ + 1) map (_ * 2) // SeqViewMM(...)
vl.force // Vector(4, 6, 8)
import collection.JavaConversions._
[b]## C25.The Architecture of Scala Collections[/b]
trait Builder[-Elem, +To] {
def +=(elem: Elem): this.type
def result(): To
}
trait CanBuildFrom[-From, -Elem, +To] {
def apply(from: From): Builder[Elem, To]
}
class TraversableLike[+Elem, +Repr]
"'same-result-type' principle: T=>T"
"""
if you want to fully integrate a new collection class
into the framework you need to pay attention to:
1. the collection should be mutable or immutable.
2. Pick the right base traits for the collection.
3. Inherit from the right implementation trait to
implement most collection operations.
4. If you want map and similar operations to return
instances of your collection type, provide an
implicit CanBuildFrom in your class’s companion object.
"""
[b]## C26.Extractors[/b]
"""
An extractor in Scala is an object that has a
method called `unapply` as one of its members.
Often, the extractor object also defines
a dual method `apply` for building values,
but this is not required.
"""
object EMail {
// The injection method (optional)
def apply(user: String, domain: String) = user +"@"+ domain
// The extraction method (mandatory)
def unapply(str: String): Option[(String, String)] = {
val parts = str split "@"
if (parts.length == 2) Some(parts(0), parts(1)) else None
}
}
"""
To make this more explicit, you could also
object EMail extends ((String, String) => String)
selectorString match { case EMail(user, domain) => ... }
==> EMail.unapply(selectorString)
EMail.unapply(EMail.apply(user, domain))
==> Some(user, domain)
"""
object Twice {
def apply(s: String): String = s + s
def unapply(s: String): Option[String] = {
val length = s.length / 2
val half = s.substring(0, length)
if (half == s.substring(length)) Some(half) else None
}
}
object UpperCase {
def unapply(s: String): Boolean = s.toUpperCase == s
}
"""
It’s possible that an extractor pattern does not bind
any variables. `unapply` method returns a boolean.
"""
def userTwiceUpper(s: String) = s match {
case EMail(Twice(x @ UpperCase()), domain) =>
"match: "+ x +" in domain "+ domain
case _ =>
"no match"
}
userTwiceUpper("DIDI@hotmail.com")
userTwiceUpper("DIDO@hotmail.com")
"return a fixed number of sub-elements in the success case."
object Domain {
// The injection method (optional)
def apply(parts: String*): String =
parts.reverse.mkString(".")
// The extraction method (mandatory)
def unapplySeq(whole: String): Option[Seq[String]] =
Some(whole.split("\\.").reverse)
}
def isTomInDotCom(s: String): Boolean = s match {
case EMail("tom", Domain("com", _*)) => true
case _ => false
}
isTomInDotCom("tom@sun.com") // true
isTomInDotCom("tom@com.net") // false
object ExpandedEMail {
def unapplySeq(email: String)
: Option[(String, Seq[String])] = {
val parts = email split "@"
if (parts.length == 2)
Some(parts(0), parts(1).split("\\.").reverse)
else
None
}
}
"Extracting with regular expressions"
val s = "tom@support.epfl.ch"
val ExpandedEMail(name, topdom, subdoms @ _*) = s
val Decimal = """(-)?(\d+)(\.\d*)?""".r
val input = "for -1.0 to 99 by 3"
for (s <- Decimal findAllIn input) println(s)
val Decimal(sign, integerpart, decimalpart) = "-1.23"
val Decimal(sign, integerpart, decimalpart) = "1.0"
[b]## C27.Annotations[/b]
import annotation._
class strategy(arg: Annotation) extends Annotation
class delayed extends Annotation
@strategy(@delayed) def f(){} // use `new` instead of `@`
@strategy(new delayed) def f(){}
"""
@deprecated("use newShinyMethod() instead")
@volatile
@serializable
@SerialVersionUID(1234)
@transient
@tailrec
@unchecked
@native
"""
[b]## C28.Working with XML[/b]
val yearMade = 1955
:paste
<a> { if (yearMade < 2000) <old>{yearMade}</old>
else xml.NodeSeq.Empty }
</a>
// :paste ctlr-d
<a> {"</a>potential security hole<a>"} </a> //escape '<','&','>'
<a> {{brace yourself!}} </a> // `{{}}` ==> `{}`
<a>Sounds <tag/> good</a>.text
<a><b><c>hello</c></b></a> \ "b"
<a><b><c>hello</c></b></a> \ "c"
<a><b><c>hello</c></b></a> \\ "c"
"""
Scala uses \ and \\ instead of XPath’s / and // .
The reason is that // starts a comment in Scala!
"""
val joe = <employee name="Joe" rank="code monkey"/>
joe \ "@name"
"""
scala.xml.XML.save("therm1.xml", node)
val loadnode = xml.XML.loadFile("therm1.xml")
"""
def proc(node: scala.xml.Node): String =
node match {
case <a>{contents}</a> => "It's an a: "+ contents
case <b>{contents @ _*}</b> => "It's a b: "+ contents
case _ => "It's something else."
}
proc(<a>apple</a>)
proc(<a>a [i]red[/i] apple</a>)
proc(<b>a [i]red[/i] apple</b>)
val catalog =
<catalog>
<cctherm>
<description>hot dog #5</description>
</cctherm>
<cctherm>
<description>Sprite Boy</description>
</cctherm>
</catalog>
catalog match {
case <catalog>{therms @ _*}</catalog> =>
for (therm <- therms)
println(": "+ (therm \ "description").text)
}
catalog match {
case <catalog>{therms @ _*}</catalog> =>
for (therm @ <cctherm>{_*}</cctherm> <- therms)
println(": "+ (therm \ "description").text)
}
[b]## C29.Modular Programming Using Objects[/b]
"self type"
class Database
class SimpleFoods
trait SimpleRecipes {
this: SimpleFoods => // @
}
"Runtime linking"
//`.type` is a singleton type
object browser {
val db:Database = null
object browser extends SimpleFoods{
val database: db.type = db // @
}
}
[b]## C30.Object Equality[/b]
"Pitfall#1: Defining equals with the wrong signature"
//An utterly wrong definition of equals
class Point(val x: Int, val y: Int){
def equals(other: Point): Boolean =
this.x == other.x && this.y == other.y
}
val p1, p2 = new Point(1, 2)
val p2a: Any = p2
p1 equals p2 // true
p1 equals p2a // false
//A better definition, but still not perfect
class Point(val x: Int, val y: Int){
override def equals(other: Any) = other match {
case that: Point => this.x == that.x && this.y == that.y
case _ => false
}
}
"Pitfall#2:Changing equals without also changing hashCode"
scala.collection.mutable.HashSet(p1) contains p2 // false
class Point(val x: Int, val y: Int) {
override def hashCode = 41 * (41 + x) + y
override def equals(other: Any) = other match {
case that: Point => this.x == that.x && this.y == that.y
case _ => false
}
}
"Pitfall#3:Defining equals in terms of mutable fields"
class Point(var x: Int, var y: Int) { // Problematic
override def hashCode = 41 * (41 + x) + y
override def equals(other: Any) = other match {
case that: Point => this.x == that.x && this.y == that.y
case _ => false
}
}
"""
Pitfall#4:Failing to define equals as an equivalence relation
relation on non-null objects
x.equals(x) ==> true
x.equals(y) if and only if y.equals(x) ==>true
if x.equals(y),y.equals(z). x.equals(z) ==>true
x.equals(y) should consistently return true
x.equals(null) should return false
"""
"-unchecked, by erasure"
abstract class Node[+T] {
def elem: T
override def equals(other: Any) = other match {
case that: Node[T] => this.elem == that.elem
//case that: Branch[t] =>
//case that: Branch[_] => //existential type
case _ => false
}
}
[b]## C31.Combining Scala and Java[/b]
[code="scala"]
"""
value types
whenever possible, the compiler translates a Scala Int
to a Java int to get better performance.
"""
"singleton objects"
object App {
def main(args: Array[String]) {
println("Hello, world!")
}
}
public final class App {
public static void main(String[] arrstring) {
App$.MODULE$.main(arrstring);
}
}
public final class App$ {
public static final App$ MODULE$ = new App$();;
private App$() {
}
public void main(String[] args) {
System.out.println("Hello, world!");
}
}
"""
Traits as interfaces
creates a Java interface of the same name.
"""
"exception"
import java.io._
class Reader(fname: String) {
private val in =
new BufferedReader(new FileReader(fname))
@throws(classOf[IOException])
def read() = in.read()
//public int read() throws java.io.IOException
}
"""
Java: Iterator<?>
Scala: Iterator[T] forSome { type T }
Scala: Iterator[_]
Java: Iterator<? extends Component>
Scala: Iterator[T] forSome { type T <: Component }
Scala: Iterator[_ <: Component]
"""
var counter = 0
synchronized {
// One thread in here at a time
counter = counter + 1
}
[b]## C32.Actors and Concurrency[/b]
"""
Java threading model based on shared data and locks
Scala share-nothing, message-passing model
"""
import scala.actors._
object SillyActor extends Actor {
def act() {
for (i <- 1 to 5) {
Thread.sleep(1000)
println("I'm acting!")
}
}
}
SillyActor.start()
import scala.actors.Actor._
val echoActor = actor {
loop {
react {
case msg:String =>
println("received string: "+ msg)
case msg:Int =>
println("received int: "+ msg)
case msg =>
println("stop, other: " + msg)
exit()
}
}
}
echoActor ! 12345
echoActor ! "hello"
echoActor ! 1.245
"""
Actors should not block
Communicate with actors only via messages
Prefer immutable messages
Make messages self-contained
"""
[b]## C33.Combinator Parsing[/b]
"Scala’s combinator parsing framework:)"
[b]## C34.GUI Programming[/b]
import swing._
import event._
object TempConverter extends SimpleSwingApplication {
def top = new MainFrame {
title = "Celsius/Fahrenheit Converter"
object celsius extends TextField { columns = 5 }
object fahrenheit extends TextField { columns = 5 }
contents = new FlowPanel {
contents += celsius
contents += new Label(" Celsius=")
contents += fahrenheit
contents += new Label(" Fahrenheit")
border = Swing.EmptyBorder(15, 10, 10, 10)
}
listenTo(celsius, fahrenheit)
reactions += {
case EditDone(`fahrenheit`) =>
val f = fahrenheit.text.toInt
val c = (f - 32) * 5 / 9
celsius.text = c.toString
case EditDone(`celsius`) =>
val c = celsius.text.toInt
val f = c * 9 / 5 + 32
fahrenheit.text = f.toString
}
}
}
TempConverter.main(Array(""))
[b]## C35.The SCells Spreadsheet[/b]
"spreadsheet can be written in just under 200 lines"
:paste
trait Arithmetic { this: Evaluator =>
operations += (
"add" -> { case List(x, y) => x + y },
"sub" -> { case List(x, y) => x - y },
"div" -> { case List(x, y) => x / y },
"mul" -> { case List(x, y) => x * y },
"mod" -> { case List(x, y) => x % y },
"sum" -> { case xs => (0.0 /: xs)(_ + _) },
"prod" -> { case xs => (1.0 /: xs)(_ * _) })
}
// a "this" type
trait Evaluator { this: Model =>
type Op = List[Double] => Double
val operations = new collection.mutable.HashMap[String, Op]
def evaluate(e: Formula): Double = try {
e match {
case Coord(row, column) => cells(row)(column).value
case Number(v) => v
case Textual(_) => 0
case Application(function, arguments) =>
val argVals = arguments flatMap evalList
operations(function)(argVals)
}
} catch {
case ex: Exception => Double.NaN
}
// takes a formula that "may" contain references to other cells and returns the value of those cells
private def evalList(e: Formula): List[Double] = e match {
case Range(_, _) => references(e) map (_.value)
case _ => List(evaluate(e))
}
// takes a formula that "may" contain references to other cells and returns references to those other cells
def references(e: Formula): List[Cell] = e match {
case Coord(row, column) =>List(cells(row)(column))
case Range(Coord(r1, c1), Coord(r2, c2)) =>
for (row <- (r1 to r2).toList; column <- c1 to c2)
yield cells(row)(column)
case Application(functrion, arguments) =>arguments flatMap references
case _ => List()
}
}
import scala.util.parsing.combinator._
object FormulaParsers extends RegexParsers {
def ident: Parser[String] = """[a-zA-Z_]\w*""".r
def decimal: Parser[String] = """-?\d+(\.\d*)?""".r
def cell: Parser[Coord] =
"""[A-Za-z]\d+""".r ^^ { s =>
val column = s.charAt(0).toUpper - 'A'
val row = s.substring(1).toInt
Coord(row, column)
}
def range: Parser[Range] =
cell ~ ":" ~ cell ^^ {
case c1 ~ ":" ~ c2 => Range(c1, c2)
}
def number: Parser[Number] =
decimal ^^ (d => Number(d.toDouble))
def application: Parser[Application] =
ident ~ "(" ~ repsep(expr, ",") ~ ")" ^^ {
case f ~ "(" ~ ps ~ ")" => Application(f, ps)
}
def expr: Parser[Formula] =
range | cell | number | application
def textual: Parser[Textual] =
"""[^=].*""".r ^^ Textual
def formula: Parser[Formula] =
number | textual | "=" ~> expr
def parse(input: String): Formula =
parseAll(formula, input) match {
case Success(e, _) => e
case f: NoSuccess => Textual("[" + f.msg + "]")
}
}
trait Formula
case class Coord(row: Int, column: Int) extends Formula {
override def toString = ('A' + column).toChar.toString + row
}
case class Range(c1: Coord, c2: Coord) extends Formula {
override def toString = c1.toString + ":" + c2.toString
}
case class Number(value: Double) extends Formula {
override def toString = value.toString
}
case class Textual(value: String) extends Formula {
override def toString = value
}
case class Application(function: String, arguments: List[Formula]) extends Formula {
override def toString = function + arguments.mkString("(", ",", ")")
}
object Empty extends Textual("")
import swing._
class Model(val height: Int, val width: Int)
extends Evaluator with Arithmetic {
case class Cell(row: Int, column: Int) extends Publisher {
private var f: Formula = Empty
def formula: Formula = f
def formula_=(f: Formula) {
for (c <- references(formula)) deafTo(c)
this.f = f
for (c <- references(formula)) listenTo(c)
value = evaluate(f)
}
private var v: Double = 0
def value: Double = v
def value_=(w: Double) {
if (!(v == w || v.isNaN && w.isNaN)) {
v = w
publish(ValueChanged(this))
}
}
override def toString = formula match {
case Textual(s) => s
case _ => value.toString
}
reactions += {
case ValueChanged(_) => value = evaluate(formula)
}
}
case class ValueChanged(cell: Cell) extends event.Event
val cells = Array.ofDim[Cell](height, width)
for (i <- 0 until height; j <- 0 until width)
cells(i)(j) = new Cell(i, j)
}
import swing._
import event._
class Spreadsheet(val height: Int, val width: Int) extends ScrollPane {
val cellModel = new Model(height, width)
import cellModel._
val table = new Table(height, width) {
rowHeight = 25
autoResizeMode = Table.AutoResizeMode.Off
showGrid = true
gridColor = new java.awt.Color(150, 150, 150)
override def rendererComponent(isSelected: Boolean, hasFocus: Boolean, row: Int, column: Int): Component =
if (hasFocus) new TextField(userData(row, column))
else
new Label(cells(row)(column).toString) {
xAlignment = Alignment.Right
}
def userData(row: Int, column: Int): String = {
val v = this(row, column)
if (v == null) "" else v.toString
}
reactions += {
case TableUpdated(table, rows, column) =>
for (row <- rows)
cells(row)(column).formula =
FormulaParsers.parse(userData(row, column))
case ValueChanged(cell) =>
updateCell(cell.row, cell.column)
}
for (row <- cells; cell <- row) listenTo(cell)
}
val rowHeader =
new ListView((0 until height) map (_.toString)) {
fixedCellWidth = 30
fixedCellHeight = table.rowHeight
}
viewportView = table
rowHeaderView = rowHeader
}
//
object Main extends SimpleSwingApplication {
def top = new MainFrame {
title = "ScalaSheet"
contents = new Spreadsheet(100, 26)
}
}
Main.main(Array(""))
// end :paste ctrl-d
"""
:"add" ,"sub" ,"div" ,"mul" ,"mod" ,"sum" ,"prod"
=add(a0,b0)
=sum(a0:d0)
=a0
"""