Scala学习笔记

Basic Types

 

string

println("""|Welcome to Ultamix 3000.

           |Type "HELP" for help.""".stripMargin)

 

Control Structures

 

for

args.foreach(arg => println(arg))

args.foreach(println)

for (arg <- args) println(arg)

for (i <- 0 to 2) print(greetStrings(i))

for (i <- 1 until 4) println("Iteration "+ i)

 

// filter, nested for

for (

    file <- filesHere

    if file.isFile

    if file.getName.endsWith(".scala")

) println(file)

 

for {

    file <- filesHere

    if file.getName.endsWith(".scala")

    line <- fileLines(file)

    trimmed = line.trim

    if trimmed.matches(pattern)

} println(file +": "+ trimmed)

 

switch

val friend =

    firstArg match {

        case"salt" => "pepper"

        case"chips" => "salsa"

        case "eggs" => "bacon"

        case _ => "huh?"

    }

 

exception

try {

    val file = new FileReader("input.txt")

} catch {

    case ex: FileNotFoundException =>

    case ex: IOException =>

}  finally {

    file.close()

}

 

Collections

 

list

val numNames = Array("zero", "one", "two")

val oneTwoThreeFour = oneTwo ::: threeFour

val oneTwoThree = 1 :: 2 :: 3 :: Nil

val empty = Nil

 

def containsNeg(nums: List[Int]) = nums.exists(_ < 0)

containsNeg(List(1, 2, 3, 4))

def containsOdd(nums: List[Int]) = nums.exists(_ % 2 == 1)

 

val List(a, b, c) = fruit

// a: String = apples

// b: String = oranges

// c: String = pears

 

val a :: b :: rest = fruit

// a: String = apples

// b: String = oranges

// rest: List[String] = List(pears)

 

val abcde = List('a', 'b', 'c', 'd', 'e')

abcde.last // res4: Char = e

abcde.init // res5: List[Char] = List(a, b, c, d)

abcde take 2 // res8: List[Char] = List(a, b)

abcde drop 2 // res9: List[Char] = List(c, d, e)

 

abcde.indices zip abcde // res17: scala.collection.immutable.IndexedSeq[(Int, Char)] = IndexedSeq((0,a), (1,b), (2,c), (3,d), (4,e))

val zipped = abcde zip List(1, 2, 3) // zipped: List[(Char, Int)] = List((a,1), (b,2), (c,3))

abcde.zipWithIndex // res18: List[(Char, Int)] = List((a,0), (b,1), (c,2), (d,3), (e,4))

 

List(List(1, 2), List(3), List(), List(4, 5)).flatten // res14: List[Int] = List(1, 2, 3, 4, 5)

 

List(1, 2, 3, 4, 5) filter (_ % 2 == 0) // List[Int] = List(2, 4)

List(1, 2, 3, 4, 5) partition (_ % 2 == 0) // (List[Int], List[Int]) = (List(2, 4),List(1, 3, 5))

List(1, 2, 3, 4, 5) find (_ % 2 == 0) // Option[Int] = Some(2)

List(1, 2, 3, -4, 5) takeWhile (_ > 0) // List[Int] = List(1, 2, 3)

List(1, 2, 3, -4, 5) dropWhile (_ > 0) // List[Int] = List(-4, 5)

List(1, 2, 3, -4, 5) span (_ > 0) // (List[Int], List[Int]) = (List(1, 2, 3),List(-4, 5))

 

// A fold left operation “(z /: xs) (op)” involves three objects: 

// a start value z, a list xs, and a binary operation op. 

// The result of the fold is op applied between successive elements of the list prefixed by z.

// For instance:

// (z /: List(a, b, c)) (op) equals op(op(op(z, a), b), c)

def sum(xs: List[Int]): Int = (0 /: xs) (_ + _)

def product(xs: List[Int]): Int = (1 /: xs) (_ * _)

 

// The operator has :\ as an analog that produces right-leaning trees. For instance:

// (List(a, b, c) :\ z) (op) equals op(a, op(b, op(c, z)))

def flattenLeft[T](xss: List[List[T]]) = (List[T]() /: xss) (_ ::: _)

 

List(1, -3, 4, 2, 6) sortWith (_ < _) // List[Int] = List(-3, 1, 2, 4, 6)

 

List.range(1, 5) // List[Int] = List(1, 2, 3, 4)

List.range(1, 9, 2) // List[Int] = List(1, 3, 5, 7)

List.range(9, 1, -3) // List[Int] = List(9, 6, 3)

 

List.fill(5)('a') // List[Char] = List(a, a, a, a, a)

List.fill(2, 3)('b') // List[List[Char]] = List(List(b, b, b), List(b, b, b))

 

List.tabulate(5)(n => n * n) // List[Int] = List(0, 1, 4, 9, 16)

 

List.concat(List('a', 'b'), List('c')) // List[Char] = List(a, b, c)

List.concat(List(), List('b'), List('c')) // List[Char] = List(b, c)

 

(List(10, 20), List(3, 4, 5)).zipped.map(_ * _) // List[Int] = List(30, 80)

(List("abc", "de"), List(3, 2)).zipped.forall(_.length == _) // Boolean = true

(List("abc", "de"), List(3, 2)).zipped.exists(_.length != _) // Boolean = false

 

map

val scores = Map[String, Int]("Abel" -> 10, "Blas" -> 100)

for ((key, value) <- scores)

 

val cs = TreeSet('f', 'u', 'n')

// cs: scala.collection.immutable.TreeSet[Char] = TreeSet(f, n, u)

 

var tm = TreeMap(3 -> 'x', 1 -> 'x', 4 -> 'x')

// tm: scala.collection.immutable.TreeMap[Int,Char] = Map(1 -> x, 3 -> x, 4 -> x)

 

tuple

val tuple = (99, "Luftballons")

println(pair._1)

println(pair._2)

 

mutable collection

import scala.collection.mutable.Set

val movieSet = Set("Hitch", "Poltergeist")

movieSet += "Shrek"

 

import scala.collection.mutable.Map

val treasureMap = Map[Int, String]()

treasureMap += (1 -> "Go to island.")

treasureMap += (2 -> "Find big X on ground.")

treasureMap += (3 -> "Dig.")

val romanNumeral = Map(1 -> "I", 2 -> "II", 3 -> "III", 4 -> "IV", 5 -> "V")

 

Functions

 

// In Scala operators are not special language syntax: any method can be an operator.

// What makes a method an operator is how you use it. When you write “s.indexOf('o')”,

// indexOf is not an operator. But when you write “s indexOf 'o'”, indexOf is an operator,

// because you’re using it in operator notation.

s indexOf ('o', 5) // Scala invokes s.indexOf(’o’, 5)

 

// Scala will transform the expression -2.0 into the method invocation “(2.0).unary_-”

// -2.0 can be change to (2.0).unary_-

-2.0

 

local functions

def processFile(filename: String, width: Int) {

    def processLine(filename: String, width: Int, line: String) {

      if (line.length > width) println(filename +": "+ line)

    }

    

    val source = Source.fromFile(filename)

    for (line <- source.getLines()) processLine(filename, width, line)

}

 

function literals

// A function literal is compiled into a class that when instantiated at run- time is a function value.

var increase = (x: Int) => x + 1

increase(10)

increase = (x: Int) => {

    println("We")

    x+1

}

 

short forms of function literals

// One way to make a function literal more brief is to leave off the parameter types.

someNumbers.filter((x) => x > 0)

// A second way to remove useless characters is to leave out parentheses around a parameter whose type is inferred.

someNumbers.filter(x => x > 0)

 

placeholder

// To make a function literal even more concise, you can use underscores as placeholders for one or more parameters

someNumbers.filter(_ > 0)

// is equivalent to the slightly more verbose x => x > 0

someNumbers.filter(x => x > 0)

 

// use underscores as placeholders for parameters

val f = (_: Int) + (_: Int) // f: (Int, Int) => Int = <function2>

 

partially applied functions

// rather than writing println(_), you could write

someNumbers.foreach(println _)

 

def sum(a: Int, b: Int, c: Int) = a + b + c

val a = sum _ // a: (Int, Int, Int) => Int = <function3>

a(1, 2, 3)

 

// the Scala compiler translates the expression a(1, 2, 3) into an invocation of

// the function value’s apply method, passing in the three arguments 1, 2, and 3.

// Thus, a(1, 2, 3) is a short form for:

a.apply(1, 2, 3)

 

val b = sum(1, _: Int, 3) // b: (Int) => Int = <function1>

b(2)

 

closures

// The function value (the object) that’s created at runtime from this function literal is called a closure.

// such as (x: Int) => x + 1, is called a closed term

var more = 1

val addMore = (x: Int) => /*bound variable*/x + /*free variable*/more

addMore(10) // 11

 

// what happens if more changes after the closure is created?

// In Scala, the answer is that the closure sees the change.

more = 9999

addMore(10) // Int = 10009

 

// Intuitively, Scala’s closures capture variables themselves, not the value to which variables refer.

// As the previous example demonstrates, the closure created for (x: Int) => x + more sees the change

// to more made outside the closure. The same is true in the opposite direction.

// Changes made by a closure to a captured variable are visible outside the closure.

val someNumbers = List(-11, -10, -5, 0, 5, 10)

var sum = 0

someNumbers.foreach(sum +=  _)

sum // Int = -11

 

special function call forms

// repeated parameters

def echo(args: String*) = for (arg <- args) println(arg)

echo("hello", "world!")

 

// this notation tells the compiler to pass each element of arr as its own argu- ment to echo,

// rather than all of it as a single argument.

val arr = Array("What's", "up", "doc?")

echo(arr: _*)

 

function value

def filesMatching(matcher: String => Boolean) =

  for (file <- filesHere; if matcher(file.getName))

    yield file

 

def filesEnding(query: String) = filesMatching(_.endsWith(query))

 

currying

// instead of one list of two Int parameters, you apply this function to two lists of one Int parameter each.

def plainOldSum(x: Int, y: Int) = x + y

def curriedSum(x: Int)(y: Int) = x + y // curriedSum: (x: Int)(y: Int)Int

 

// what’s happening here is that when you invoke curriedSum, you actually get two traditional function

// invocations back to back. The first function invocation takes a single Int parameter named x,

// and returns a function value for the second function. This second function takes the Int parameter y.

curriedSum(1)(2)

 

// here’s a function named first that does in spirit what the first traditional function

// invocation of curriedSum would do:

def first(x: Int) = (y: Int) => x + y

val second = first(1)

second(2)

 

// You can use the placeholder notation to use curriedSum in a partially applied function expression

// The underscore in curriedSum(1)_ is a placeholder for the second parameter list

val onePlus = curriedSum(1)_

onePlus(2)

 

writing new control structures

def twice(op: Double => Double, x: Double) = op(op(x))

twice(_ + 1, 5) // Double = 7.0

 

def withPrintWriter(file: File, op: PrintWriter => Unit) {

  val writer = new PrintWriter(file)

  try {

    op(writer)

  } finally {

    writer.close()

  }

}

 

withPrintWriter(new File("date.txt"), writer => writer.println(new java.util.Date))

 

// use curly braces instead of parentheses to surround the argument list

def withPrintWriter(file: File)(op: PrintWriter => Unit) {

  val writer = new PrintWriter(file)

  try {

    op(writer)

  } finally {

    writer.close()

  }

}

 

withPrintWriter(new File("date.txt")) {

    writer => writer.println(new java.util.Date)

}

 

variable arguments

def funcWhichTakesSeq(seq: Any*) = println(seq.length + ": " + seq)

val seq = List(1, 2, 3)

funcWhichTakesSeq(seq) //1: Array(List(1, 2, 3)) -i.e. a Seq with one entry

funcWhichTakesSeq(seq: _*) //3: List(1, 2, 3)

 

Class

 

// basic class definition

class ChecksumAccumulator {

    private var sum = 0

    def add(b: Byte): Unit = sum += b

    def checksum(): Int = ~(sum & 0xFF) + 1

}

val acc = new ChecksumAccumulator

 

// When a singleton object shares the same name with a class, it is called that class’s companion object.

// You must define both the class and its companion object in the same source file.

// The class is called the companion class of the singleton object. A class and its companion object

// can access each other’s private members.

import scala.collection.mutable.Map

object ChecksumAccumulator {

    private val cache = Map[String, Int]()

    def calculate(s: String): Int =

        ......

}

 

// parameters validate, constructor, function operator, toString

class Rational(n: Int, d: Int) {

    require(d != 0)

    val numer: Int = n

    val denom: Int = d

 

    def this(n: Int) = this(n, 1)

    def + (that: Rational): Rational = new Rational( numer * that.denom + that.numer * denom, denom * that.denom )

    def + (i: Int): Rational = new Rational(numer + i * denom, denom)

    override def toString = n +"/"+ d

}

 

// Implicit conversions.

// The problem here is that 2 * r is equivalent to 2.*(r),

// so it is a method call on the number 2, which is an integer. But the Int class contains no

// multiplication method that takes a Rational argument

val r = new Rational(2,3)

2 * r

 

// You can create an implicit conversion that automatically converts integers to rational numbers when needed.

// This defines a conversion method from Int to Rational. The implicit modifier in front of the method 

// tells the compiler to apply it automatically in a number of situations.

implicit def intToRational(x: Int) = new Rational(x)

 

// For instance, the Rocket class above can access method fuel, which is declared private in object Rocket.

// Analogously, the Rocket object can access the private method canGoHomeAgain in class Rocket.

class Rocket {

  import Rocket.fuel

  private def canGoHomeAgain = fuel > 20

}

object Rocket {

  private def fuel = 10

  def chooseStrategy(rocket: Rocket) {

    if (rocket.canGoHomeAgain)

      goHome()

    else

      pickAStar()

  }

  def goHome() {}

  def pickAStar() {}

}

 

Private constructors and factory methods

 

// Hiding a primary constructor by making it private.

class Queue[T] private (

  private val leading: List[T],

  private val trailing: List[T])

 

// error: constructor Queue cannot be accessed in object $iw new Queue(List(1, 2), List(3))

new Queue(List(1, 2), List(3))

 

// One possibility is to add an auxiliary constructor

def this() = this(Nil, Nil)

 

// Another possibility is to add a factory method that builds a queue from such a sequence of initial elements.

object Queue {

  // constructs a queue with initial elements ‘xs’

  def apply[T](xs: T*) = new Queue[T](xs.toList, Nil)

}

 

// An alternative: private classes

// hide the class itself and only export a trait that reveals the public interface of the class

trait Queue[T] {

  def head: T

  def tail: Queue[T]

  def enqueue(x: T): 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 mirror = if (leading.isEmpty) new QueueImpl(trailing.reverse, Nil) else this

 

    def head: T = mirror.leading.head

 

    def tail: QueueImpl[T] = {

      val q = mirror

      new QueueImpl(q.leading.tail, q.trailing)

    }

 

    def enqueue(x: T) = new QueueImpl(leading, x :: trailing)

  }

}

 

Traits

 

// A trait definition looks just like a class definition except that it uses the keyword trait

trait Philosophical {

  def philosophize() {

    println("I consume memory, therefore I am!")

  }

}

 

// it can be mixed in to a class using either the extends or with keywords

class Frog extends Philosophical {

  override def toString = "green"

}

 

Packages and Imports

 

Putting code in packages

package bobsrockets.navigation {

  class Navigator

}

 

package bobsrockets {

  package navigation {

    class Navigator {

      // No need to say bobsrockets.navigation.StarMap

      val map = new StarMap

    }

    class StarMap

  }

  class Ship {

    // No need to say bobsrockets.navigation.Navigator

    val nav = new navigation.Navigator

  }

  package fleets {

    class Fleet {

      // No need to say bobsrockets.Ship

      def addShip() { new Ship }

    }

  }

}

 

// import selector clause enclosed in braces, which follows the object from which members are imported.

import Fruits.{Apple, Orange}

// the Apple object is renamed to McIntosh

import Fruits.{Apple => McIntosh, Orange}

// This imports all members from object Fruits but renames Apple to McIntosh

import Fruits.{Apple => McIntosh, _}

// This imports all members of Fruits except Pear

import Fruits.{Pear => _, _}

 

Package objects

// Syntactically, a package ob- ject looks much like one of the curly-braces packagings shown earlier in the chapter.

// The only difference is that it includes the object keyword. It’s a package object, not a package.

package object clay {

  println("init package...")

  def show = println("package clay")

}

 

import clay.show

 

object Demo {

  def main(args: Array[String]) {

    show

  }

}

 

Case Classes and Pattern Matching

 

// First, it adds a factory method with the name of the class. This means you can write say,

// Var("x") to construct a Var object instead of the slightly longer new Var("x").

// The second syntactic convenience is that all arguments in the parameter list of a case class implicitly get a val prefix.

// Third, the compiler adds “natural” implementations of methods toString, hashCode, and equals to your class.

// They will print, hash, and compare a whole tree consisting of the class and (recursively) all its arguments.

abstract class Expr

case class Var(name: String) extends Expr

 

Kinds of patterns

def describe(x: Any) = x match {

  case 5 => "five"

  case true => "truth"

  case "hello" => "hi!"

  case Nil => "the empty list"

  case _ => "something else"

}

 

expr match {

  case 0 => "zero"

  case somethingElse => "not zero: "+ somethingElse

}

 

expr match {

  case BinOp("+", e, Number(0)) => println("a deep match")

  case _ =>

}

 

expr match {

  case List(0, _, _) => println("found it")

  case _ =>

}

 

expr match {

  case List(0, _*) => println("found it")

  case _ =>

}

 

expr match {

  case (a, b, c) => println("Tuple patterns "+ a + b + c)

  case _ => 

}

 

def generalSize(x: Any) = x match {

  case s: String => s.length

  case m: Map[_, _] => m.size

  case _ => -1

}

 

Pattern guards

// A pattern guard comes after a pattern and starts with an if.

// The guard can be an arbitrary boolean expression, which typically refers to variables in the pattern.

// If a pattern guard is present, the match succeeds only if the guard evaluates to true.

def simplifyAdd(e: Expr) = e match {

  case BinOp("+", x, y) if x == y => BinOp("*", x, Number(2))

  case _ => e

}

 

// match only positive integers

case n: Int if 0 < n => ...

// match only strings starting with the letter ‘a’

case s: String if s(0) == 'a' => ...

 

Sealed classes

// A sealed class cannot have any new subclasses added except the ones in the same file.

// If you match against case classes that inherit from a sealed class,

// the compiler will flag missing combinations of patterns with a warning message.

sealed abstract class Expr

case class Var(name: String) extends Expr

case class Number(num: Double) extends Expr

case class UnOp(operator: String, arg: Expr) extends Expr

case class BinOp(operator: String, left: Expr, right: Expr) extends Expr

 

// You will get a compiler warning like the following:

// warning: match is not exhaustive!

// missing combination           UnOp

// missing combination          BinOp

def describe(e: Expr): String = e match {

  case Number(_) => "a number"

  case Var(_)    => "a variable"

}

 

def describe(e: Expr): String = (e: @unchecked) match {

  case Number(_) => "a number"

  case Var(_)    => "a variable"

}

 

// pattern for list

def rev[T](xs: List[T]): List[T] = xs match {

  case List() => xs

  case x :: xs1 => rev(xs1) ::: List(x)

}

 

Patterns everywhere

val myTuple = (123, "abc")

// number: Int = 123

// string: java.lang.String = abc

val (number, string) = myTuple

 

val exp = new BinOp("*", Number(5), Number(1))

// op: String = *

// left: Expr = Number(5.0)

// right: Expr = Number(1.0)

val BinOp(op, left, right) = exp

 

Case sequences as partial functions

val withDefault: Option[Int] => Int = {

  case Some(x) => x

  case None => 0

}

 

withDefault(Some(10)) // res28: Int = 10

withDefault(None)     // res29: Int = 0

 

Patterns in for expressions

val results = List(Some("apple"), None, Some("orange"))

for (Some(fruit) <- results) println(fruit)

 

Stateful Objects

 

Reassignable variables and properties

 

// Defining getter and setter methods directly.

class Time {

  private[this] var h = 12

  private[this] var m = 0

 

  def hour: Int = h

  def hour_=(x: Int) {

    require(0 <= x && x < 24)

    h = x

  }

 

  def minute = m

  def minute_=(x: Int) {

    require(0 <= x && x < 60)

    m = x

  }

}

 

// Defining a getter and setter without an associated field.

class Thermometer {

  // It is 0 for numeric types, false for booleans, and null for reference types.

  // This is the same as if the same variable was defined in Java without an initializer.

  var celsius: Float = _

 

  def fahrenheit = celsius * 9 / 5 + 32

  def fahrenheit_=(f: Float) {

    celsius = (f - 32) * 5 / 9

  }

 

  override def toString = fahrenheit + "F/" + celsius + "C"

}

 

Type Parameterization

 

Variance annotations

 

class Variance {

  class A

  class B extends A

  class C extends B

 

  // generic types have by default nonvariant subtyping

  class Nonvariant[T] {}

 

  // Prefixing a formal type parameter with a + indicates that subtyping is covariant (flexible) in that parameter.

  // By adding this single character, you are telling Scala that you want Queue[String],

  // for example, to be considered a subtype of Queue[AnyRef].

  // The compiler will check that Queue is defined in a way that this subtyping is sound.

  class Covariant[+T] {}

 

  // then if T is a subtype of type S, this would imply that Queue[S] is a sub- type of Queue[T]

  class Contravariant[-T] {}

 

  def nonvariant(arg: Nonvariant[B]) = println(arg)

  def covariant(arg: Covariant[B]) = println(arg)

  def contravariant(arg: Contravariant[B]) = println(arg)

 

  def test {

    this.nonvarint(new Nonvariant[B])

    // this.nonvariant(new Nonvariant[C]) error

 

    this.covariant(new Covariant[B])

    this.covariant(new Covariant[C])

 

    this.contravariant(new Contravariant[B])

    this.contravariant(new Contravariant[A])

    // this.contravariant(new Contravariant[C]) error

  }

}

 

你可能感兴趣的:(scala)