欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

004.《Programming in Scala,2nd》干货摘要

程序员文章站 2023-12-28 08:03:22
...
004.《Programming in Scala,2nd》干货摘要
            
    
    博客分类: 攻城狮朋友圈读书笔记 读书笔记Scala代码 
原文:http://www.moilioncircle.com/manshow/004.programming-in-Scala-2nd.html
题图:Scala之父'Martin Odersky'的书,十分精彩和经典,单词量大概4~5k,800多页,推荐进阶阅读。


# 004.《Programming in Scala,2nd》干货摘要

《Programming in Scala,2nd》的天书级的干货摘要, 
建议运行并细看,遇到看不懂的,可以认为知识点不扎实了。 

@史荣久 / 2015-06-09 / CC-BY-SA-3.0 

## 英文的,值得细看

全书单词量,大概5k吧,像我CET4+Dict,能够啃下来。 
技术书籍的语法和词汇都简单,看不懂也猜得出的。

不要以为看过本文,就不用看书了,每人每次看书的收获不同。 
书中的编程例子特别好,尤其最后200行代码写出个excel。

还有本书叫《Programming Scala》,中间没有in。 
书我没看,不知如何。只想说下,本文不是这本书的004.《Programming in Scala,2nd》干货摘要
            
    
    博客分类: 攻城狮朋友圈读书笔记 读书笔记Scala代码 

# 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]

 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
"""


[b]## C6.Functional Objects[/b]

 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
"""
 

[b]## C7.Built-in Control Structures[/b]

 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
"""


[b]## C8.Functions and Closures[/b]

 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)


[b]## C9.Control Abstraction[/b]

 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


[b]## C10.Composition and Inheritance[/b]

 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)


[b]## C11.Scala’s Hierarchy[/b]

 scala
"""
    |<-AnyVal<-(Unit,Int,Double...)<-Nothing
Any |                                   v
    |<-AnyRef<-(Seq,Set,Map,String...)<-Null
"""

[b]## C12.Traits[/b]

 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.
"""


[b]## C13.Packages and Imports[/b]

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


## C14.Assertions and Unit Testing

"""
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
"""


## C15.Case Classes and Pattern Matching

//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)


## C16.Working with Lists

"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))


## C17.Collections

"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
"""


## C18.Stateful Objects

class Time {
  private[this] var h = 12
  def hour: Int = h
  def hour_= (x: Int) {
    require(0 <= x && x < 24)
    h = x
  }
}


## C19.Type Parameterization

"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
}


## C20.Abstract Members

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 !!"


## C21.Implicit Conversions and Parameters

"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`"


## C22.Implementing Lists

"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
"""


## C23.For Expressions Revisited

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
}


## C24.The Scala Collections API

"""
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._


## C25.The Architecture of Scala Collections

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.
"""


## C26.Extractors

"""
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"



## C27.Annotations

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
"""


## C28.Working with XML

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)
}


## C29.Modular Programming Using Objects

"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 // @
  }
}


## C30.Object Equality

"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]

"""
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
}


## C32.Actors and Concurrency

"""
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
"""


## C33.Combinator Parsing

 "Scala’s combinator parsing framework:)"


## C34.GUI Programming

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(""))


## C35.The SCells Spreadsheet

"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
"""

上一篇:

下一篇: