我爱编程

Scala学习笔记 - A1篇

2018-08-09  本文已影响0人  hakase_nano

教材:快学Scala
markdown阅读:https://www.zybuluo.com/mdeditor

chapter 1. 基础

1.2 声明值(val)和变量(var)

val xmax, ymax = 100 // 将xmax和ymax设为100
var greeting, message: string = null // greeting和message都是字符串,被初始化为null

1.3 常用类型

1.toString() // return "1"
1.to(10) // return Range(1,2,3,4,5,6,7,8,9,10)
"Hello".intersect("World") // 求交集 return "lo"

更多:

1.4 算术和操作符重载

1.5 调用函数和方法

import scala.math._ // _是Scala的通配符,类似Java的*
min(3, Pi)
pow(2, 4)

1.6 apply方法

chapter 2. 控制结构和函数

2.1 条件表达式(if)

val s = if (x > 0) 1 else -1 // preferred, val s, type: Int
// 等价于
if (x > 0) s = 1 else s = -1 // var s

2.2 语句终止 每行末尾的分号不是必须的

2.3 块表达式和赋值

2.4 输入和输出

2.5 循环

val s = "Hello"
var sum = 0
for (i <- 0 until s.length) // i 最后一个取值是s.length - 1
     sum += s(i)
// 等价于
for (ch <- s) sum += ch

2.6 高级for循环/ for推导式

for (i <- 1 to 3; j <- 1 to 3) print((10*i+j) + " ") // print 11 12 13 21 22 23 31 32 33
for (i <- 1 to 3; j <- 1 to 3 if i != j) print((10*i+j) + " ") // print 12 13 21 23 31 32
for (i <- 1 to 3; from = 4 - i; j <- from to 3) print((10*i+j) + " ") // print 13 22 23 31 32 33
for (i <- 1 to 5) yield i % 3 // return Vector(1,2,0,1,2)
for (c <- "hello"; i <- 0 to 1) yield (c+i).toChar // ??? res40: String = hieflmlmop
for (i <- 0 to 1; c <- "hello") yield (c+i).toChar // ??? res41: scala.collection.immutable.IndexedSeq[Char] = Vector(h, e, l, l, o, i, f,  m, m, p)

2.7 函数

def fac(n: Int) = { // 非递归函数不需要指定返回类型
     var r = 1
     for (i <- 1 to n) r = r * i
     r // 不需要return
}

2.9 变长参数

def sum(args: Int*) = {
     var res = 0
     for (arg <- args) res += arg
     res
}
val s1 = sum(1, 4, 9, 16, 24) // 参数为Seq类型
val s2 = sum(1 to 5)       // wrong, 单个参数输入时,必须是Int类型
val s3 = sum(1 to 5: _*) // correct, 将Range当做**参数序列**处理
def recursiveSum (args: Int*): Int = {
     if (args.length == 0) 0
     else args.head + recursiveSum(args.tail: _*) // head:第一个元素 tail: 其他元素
}

2.10 懒值 lazy val 推迟初始化

val words         = scala.io.Source.fromFile("/usr/share/dict/words").mkString // 在words被定义时即被取值
lazy val words = scala.io.Source.fromFile("/usr/share/dict/words").mkString // 在words首次使用时被取值
def words        = scala.io.Source.fromFile("/usr/share/dict/words").mkString // 在每一次words被使用时取值

chapter 3. 数组相关操作

3.1 定长数组 Array

val nums = new Array[Int](10)  // 长度为10,初始化为0
val a = new Array[String](10) // 长度为10,初始化为null
val s = Array("hello", "world") // 长度为2,Array[String](类型推断)已提供初始值不需要new
s(0) = "goobye" // Array("goobye", "world")

3.2 变长数组 ArrayBuffer

类似C++的vector,Java的ArrayList

import scala.collection.mutable.ArrayBuffer
val b = ArrayBuffer[Int]()
b += 1 // ArrayBuffer(1)
b += (1, 2, 3, 5) // ArrayBuffer(1, 1, 2, 3, 5)
b ++= Array(8, 13, 21) // ArrayBuffer(1, 1, 2, 3, 5, 8, 13, 21)
b ++= Seq(23, 25) // ArrayBuffer(1, 1, 2, 3, 5, 8, 13, 21, 23, 25)
b.trimEnd(7) // ArrayBuffer(1, 1, 2)
val bArr = b.toArray // Array(1, 1, 2) 转换为Array
val bArrBuf = bArr.toBuffer // ArrayBuffer(1, 1, 2) 转换为ArrayBuffer

3.3 遍历

0 until 5 // Range(0, 1, 2, 3, 4)
0 until (5, 2) // Range(0, 2, 4)
(0 until 5).reverse // Range(4, 3, 2, 1, 0)

3.4 数组转换

for推导式:for(...) yield 循环创建一个类型与原始集合相同的新集合

for (i <- 1 to 5) yield i % 3 // return Vector(1,2,0,1,2)
for (c <- "hello"; i <- 0 to 1) yield (c+i).toChar // res40: String = hieflmlmop
for (i <- 0 to 1; c <- "hello") yield (c+i).toChar // res41: scala.collection.immutable.IndexedSeq[Char] = Vector(h, e, l, l, o, i, f,  m, m, p)

3.5 常用算法

很大比例的业务运算不过是在求和排序**
It is often said that a large percentage of business computations are nothing but computing sums and sorting.

Array(1, 7, 2, 9).sum // 19 ArrayBuffer同样适用
Array("Mary", "had", "a", "little", "lamb").max // 'M' < 'a' < 'h' 'l'
val b = ArrayBuffer(1, 7, 2, 9)
val bSorted = b.sorted // ArrayBuffer(1, 2, 7, 9) ,b没有被改变
val bDesc = b.sortWith(_ > _) // ArrayBuffer(9, 7, 2, 1)

可以直接对一个 Array 排序,但不能对 ArrayBuffer 排序

val a = b.toArray
scala.util.Sorting.quickSort(a) // Array(1, 2, 7, 9)

mkString 将数组转换为字符串(类似js的join)
对于 min max quickSort 方法,元素类型必须支持比较操作,即带有 Ordered trait的类型

a.mkString(" and ") // "1 and 2 and 7 and 9"
a.mkString("<", ",", ">") // "<1,2,7,9>"

3.6 实用的Scaladoc解码指环 (page.36)

3.7 多维数组

val matrix = Array.ofDim[Int](3, 4) // 3行 * 4列
matrix(row)(col) = 42 // 访问元素

chapter 4. 映射和元组 Maps and Tuples

A classic programmer’s saying is, “If you can only have one data structure, make it a hash table.”

4.1 构造Map

4.2 获取Map中的值

val bobs = scores("Bob")
val bobs = scores.getOrElse("Bob", 0)
val bobs = scores.get("Bob") // res17: Option[Int] = Some(3)
val bobs = scores.get("Bobxxx") // res18: Option[Int] = None

4.3 更新Map中的值

scores("Bob") = 10 // update
scores("Fred") = 7 // add
// 等价于
scores += ("Bob" -> 10, "Fred" -> 7)
scores -= "Alice" // delete
val newScores = scores + ("Bob" -> 10, "Fred" -> 7)
val newScores = scores - "Alice"

4.4 迭代Map

for ((k, v) <- scores) print(k, v)
scores.keySet // res47: scala.collection.Set[String] = Set(Bob, Fred, Alice, Cindy)
scores.keys   // res49: Iterable[String] = Set(Bob, Fred, Alice, Cindy)
scores.values // res48: Iterable[Int] = HashMap(3, 10, 10, 8)
for (v <- scores.values) print(v + " ")
for ((k, v) <- scores) yield (v, k) // 交换key和value位置

4.5 Sorted Map

4.7 tuple

val tp = (1, 3.14, "Fred") // tp: (Int, Double, String) = (1,3.14,Fred)
val second = tp._2 // second: Double = 3.14
val (first, second, third) = tp // first: Int = 1 second: Double = 3.14 third: String = Fred
val (first, second, _) = tp // 不关心的组元在其位置上使用_

4.8 zip操作

val symbols = Array("<", "=", ">")
val counts = Array(2, 10, 2)
val pairs = symbols.zip(counts) // Array[(String, Int)] = Array((<,2), (=,10), (>,2))
for((s, n) <- pairs) print(s * n) // <<==========>>
val pairsMap = symbols.zip(counts).toMap // pairsMap: scala.collection.immutable.Map[String,Int] = Map(< -> 2, = -> 10, > -> 2)

chapter 5. 类 Class

5.1 简单类和无参方法

class Counter {
     private var value = 0 // **必须**初始化字段
     def inc() { value += 1 } // 无参方法,**默认公有**
     def current() = value // 无参方法+1
}

val myCounter = new Counter // 构造实例
myCounter.inc() // **改值器** 无参方法,后面加()
println( myCounter.current ) // **取值器** 无参方法,不加()

5.2 带getter和setter的属性

println(fred.age) // 调用方法fred.age
fred.age = 21 // 调用方法fred.age=(21)

5.3 只带getter的属性 — 只读属性的val字段

5.4 对象私有字段(Object-Private)

5.6 辅助构造器

class Person {
    private var name = ""
    private var age = 0
    def this(name: String) { // 一个辅助构造器
        this() //调用主构造器
        this.name = name
    }
    def this(name: String, age: Int) { // 另一个辅助构造器
        this(name) // 调用一个辅助构造器
        this.age = age
    }
}
val p1 = Person()
val p2 = Person("Fred")
val p3 = Person("Petr", 20)

5.7 主构造器

class Person(val name: String, val age: Int) {
    println("Just constructed another person") // 每次有对象构造出来都会执行
    def this(name: String) {
        this(name, 0)
    }
    def this(bflag: Boolean) {
        this() // *出错* 定义带参数主构造器后,将没有默认的无参构造器
    }
}
val p3 = new Person() // *出错* 没有无参构造器
val p1 = new Person("hello") // 输出 "Just constructed another person"
val p2 = new Person("world", 10) // // 输出 "Just constructed another person"

5.8 嵌套类

在类里面定义类,默认对象A的嵌套类和对象B的嵌套类互不相同

import scala.collection.mutable.ArrayBuffer
class Network {
    class Member(val name: String) {
        val contacts = new ArrayBuffer[Member]
    }
    private val members = new ArrayBuffer[Member]
    def join(name: String) = {
        val m = new Member(name)
        members += m
        m
    }
}
val chatter = new Network // 嵌套类 chatter.Member
val myFace = new Network // 嵌套类 myFace.Member
// chatter.Member 和 myFace.Member 是两个不同的类
val fred = chatter.join("Fred")
val joe = chatter.join("Joe")
fred.contacts += joe // OK, fred 和 joe 都是chatter.Member类
val spinoza = myFace.join("Spinoza")
fred.contacts += spinoza // *出错* fred是chatter.Member类,spinoza是myFace.Member类
object Network {
    class Member(val name: String) {...}
}
class Network {
    private val members = new ArrayBuffer[Network.Member]
    ...
}
class Network {
    class Member(val name: String) {
        val contacts = new ArrayBuffer[Network#Member]
    }
    ...
}
class Network(val name: String) { outer =>
    class Member(val name: String) {
        def desc = name + " inside " + outer.name
    }
}

chapter 6. 对象 Object

object语法的用处
- 需要某个类的单个实例
- 为其他值或函数找一个可以挂靠的地方(when you want to find a home for miscellaneous values or functions)

6.1 单例对象

object Accounts {
    private var lastNum = 0
    def newUniqueNum() = { lastNum += 1; lastNum }
}
Account.newUniqNum() // 当需要一个新的唯一帐号时调用, 第一次调用前构造器不会执行

6.2 伴生对象 Companion Object

class Account {
    // 可以调用伴生对象的私有方法
    // 这里Account.是必须的,因为伴生对象的方法不在此作用域中
    val id = Account.newUniqueNum()
    private var balance = 0.0
    def deposit(amount: Double) { balance += amount }
}
object Accounts {
    private var lastNum = 0
    private def newUniqueNum() = { lastNum += 1; lastNum }
}

6.3 继承类/Trait的对象 Objects Extending a Class or Trait

6.4 apply方法

abstract class UnitConversion(val fromUnitName: String, val toUnitName: String) {
    def convert(fromUnit: Double): Double // 包含定义不完整的方法,故为抽象类
    def apply(fromUnit: Double): Double = { // apply最终是在派生的object中使用
        println(fromUnit.toString + " " + fromUnitName + " = " + convert(fromUnit).toString + " " + toUnitName)
        convert(fromUnit)
    }
}
object InchesToCentimeters extends UnitConversion("inches", "centimeters") {
     // override def 重载父类方法
    override def convert(fromUnit: Double): Double = 2.54 * fromUnit
}
object GallonsToLiters extends UnitConversion("gallons", "liters") {
    override def convert(fromUnit: Double): Double = 3.785 * fromUnit
}
object FahrenheitToCelsius extends UnitConversion("fahrenheit", "celsius") {
    override def convert(fromUnit: Double): Double = (fromUnit - 32) / 1.8
}
InchesToCentimeters(1) // 调用 InchesToCentimeters.apply(1)
GallonsToLiters(2)
FahrenheitToCelsius(86)

6.5 应用程序对象 Application Objects

Scala的Hello World程序,从一个对象的main开始,类型Array[String] => Unit

object Hello {
     def main(args: Array[String]) {
          println("Hello World")
     }
}

继承App trait,可以不用定义main函数,直接把main代码加到object构造器方法体内,即object的定义内
原理:App extends DelayedInit(也是一个trait),所有带有DelayedInit 这个trait的类,其初始化方法都会被挪到delayedInit方法中。App trait的main方法捕获命令行参数,调用delayedInit方法。

object Hello extends App {
     println("Hello World")
}

6.6 枚举

Scala没有自带枚举类型,可通过Enumeration辅助生成枚举类型

object PokerSuits extends Enumeration {
    type PokerSuits = Value // 声明了该枚举类型的名字为PokerSuits.PokerSuits,若不加这行则枚举类型为PokerSuits.Value
    val Spade = Value("♠") // 每个枚举量的声明,参数为枚举的名称(可选),ID(可选),默认从0开始
    val Heart = Value("♥") // id=1
    val Club = Value("♣") // id=2
    val Dimond = Value("♦") // id=3
}
import PokerSuits._  // 使用前要先import
def isRed(suite: PokerSuits.PokerSuits): Boolean = suite == Heart || suite == Dimond
isRed(Heart) // true
Spade.id // 1
Club.toString // ♣

chapter 7. 包和引入 Packages and Imports

7.1 包 Packages

7.2 作用域规则 Scopes

package x {
    object obj_top {
        def fn = println("o1 func")
    }
    package y {
        package z {
            object obj {
                def fn = {
                    println("o2 func")
                    obj_top.fn
                }
            }
        }
    }
    package w {
        object obj {
            def fn = {
                println("o3 func")
                obj_top.fn
                y.z.obj.fn
            }
        }
    }
}
object Chap7Packages extends App {
    x.w.obj.fn
}

7.3 链式包语句

以串联的形式声明package,被串联在中间的package的成员将不能用相对路径表示

package x.y {
    object obj {
        def fn = obj_top.fn // *错误* 位于包x的obj_top不能直接可见
        def fn = x.obj_top.fn // ok
    }
}

7.4 文件顶部标记法

适用于文件中所有的代码属于同一个包

package x.y
package z
object obj {}
// 以上代码等价于
package x.y {
     package z {
          object obj {}
     }
}

7.5 包对象

package object x { val defaultName = "xxx" }
package x {
     object obj_top {
          def fn = println( defaultName ) // 在包里面不需要加x.
     }
}
println( x.defaultName ) // 在包外面加上x. 就像是包里面直接定义的常量

7.6 包可见性

可以通过在public/protect/private加入限定包名称,控制类成员在不同包层级的可见性

package y {
    package z {
        object obj {
            private[z] def fn = {} // 只在包z中可见
            private[y] def fn2 = {} // 可见度扩展到y
        }
    }
}

7.7 引入 import

import java.lang._
import scala._
import Predef._

chapter 8. 继承 Inheritance

8.1 扩展类 Extending a Class

class Employee extends Person {...}
可将类/方法/字段声明为 final,确保其不能被派生/重写

8.2 重写方法 Overriding Methods

8.3 类型检查和转换

if (p.isInstanceOf[Employee]) { // true 当p非null,且是Employee类或其子类的对象
     val s = p.asInstanceOf[Employee] // s类型为Employee,若p不是一个Employee则抛出异常
}
p match {
     case s: Employee => ... // s 作为Employee处理
     case _ => ... // p 不是Employee
}

8.4 protected字段和方法

8.5 父类构造器

// 1. 定义了子类Employee
// 2. 调用了父类构造器Person(name, age)
`class Emploee(name: String, age: Int, val salavy: Double) extends Person(name, age) {...}

8.6 重写字段

8.7 匿名子类

val alien = new Person("Fred") {
     def greeting = "I am alien Fred."
}
def meet(p: Person{def greeting: String}) = println(p.name + "says: " + p.greeting)

匿名子类的类型为 Person{def greeting: String}

8.8 抽象类

8.10 构造顺序和提前定义

8.11 Scala继承层级

def printUnit(x: Unit) = println(x)
printUnit("hello") // “hello"被替换成(),打印出()

8.12 对象相等性

final override def equals (other: Any) = {
     val that = other.asInstanceOf[Item]
     if (that == null) false
     else desc == that.desc && price == that.price
}

Chapter 9. 文件和正则表达式

9.1 读取行/字符

import scala.io.Source
val source = Source.fromFile("myfile.txt", "UTF-8")
val source1 = Source.fromURL("http://horstmann.com", "UTF-8") // 从URL读取
val source2 = Source.fromString("hello world") // 从String读取,调试时很有用
val lines = source.getLines.toArray // 读取行
for (c <- source) {...} // 处理字符
source.buffered // 查看某个字符但不处理
...
source.close() // 处理完毕要close

9.8 序列化 Serialization

9.9 执行Shell命令

原理:sys.process 包含一个从String到 ProcessBuilder 对象的隐士转换,! 操作符执行的是隐式转换后的 ProcessBuilder 对象

import sys.process._
"ls -al .." ! // 执行结果打印到标准输出,返回结果:被执行命令的返回值,成功0
val res = "ls -al .." !! // 执行结果以字符串形式返回
"ls -al .." #| "grep sec" ! // 管道

还有重定向 #> #>> #<,以及 p #&& q(如果p成功则执行q) p #|| q(如果p失败则执行q)

9.10 正则表达式

val numPattern = "[0-9]+".r
val wsnumwsPattern = """\s+[0-9]+\s+""".r // 用"""不需要转义反斜杠
val matches = numPattern.findAllIn("99 bottles, 98 bottles").toArray // Array(99, 98)
val m1 = wsnumwsPattern.findFirstIn("99 bottles, 98 bottles").toArray // Some(" 98 ")

还有方法:findPrefixOf replaceFirstIn replaceAllIn (略)

9.11 正则表达式组

val numitemPattern = "([0-9]+) ([a-z]+)".r
val numitemPattern(num, item) = "99 bottles" // num: String = 99, item: String = bottles
for (numitemPattern(num, item) <- numitemPattern.findAllIn("99 bottles, 98 bottles")) println((num, item))
上一篇 下一篇

猜你喜欢

热点阅读