快学scala

1 篇文章 0 订阅

第一章 基础

操作符
1.+(2) 和 1 + 2 的效果一样, +是int类型的一个方法, 你可以重载它, 然后用着两种语法调用

导入
import scala.math._  是导入math包中的所有方法

方法调用
"hello".distinct  通常来说, 不带参数切不改变原来对应的方法不带括号

apply
"hello"(4) 是 "hello".apply(4)的简写, 可以理解为实现了apply方法. 就可以直接重载()

第二章 控制结构和函数

条件表达式
var s = if (2 > 1) 1 else 0  # 条件表达式会返回值
var s = -1; if (2 > 1) s = 1 else s = 0  # 也可以在表达式内部赋值
var s = if (2 < 1) 1  # 得到的值是 AnyVal = (), 缺失else语句会自动补全为 else ()


块表达式
val distance = {
    val dx = 1
    val dy = 2
    dx + dy
}
块中最后一个表达式的值就是块的值


循环
while (n > 0) {
    n -= 1
}

for (i <- 1 to 10) {
    print(i)
}

for (i <- 1 until 10) {  // 只会遍历到9
    print(i)
}

for (i <- "hello") {  // 遍历字符串
    print(i)
}

scala并没有breakcontinue, 如果想退出循环, 可以有以下选择
1. 使用Boolean类型的控制变量
2. 将循环写在嵌套函数中, 必要时return
3. 使用Breaks中的break方法, 不过这个方法是通过捕获异常完成的, 效率较低
import scala.util.control.Breaks.break
for (i <- 1 to 10){
    println(i)
    break
}


高级for循环  
for (i <- 1 to 3; j <- 1 to 3) {  // 多个生成器
    println(i, j)
}

for (i <- 1 to 3; j <- 1 to 3 if i != j) {  // 生成器后可以跟一个boolean表达式
    println(i, j)
}

for (i <- 1 to 3; from = 4 - i; j <- from to 3) {  // 可以定义循环中使用的变量
    println(i, j)
}


for推导式
for (i <- 1 to 7) yield {  // 循环体以yield开始, 会尝试构造一个集合
    1 % 3
}

for (c <- "hello"; i <- 0 to 1) yield (c + i).toChar  //String = hieflmlmop,  for推导式生成的集合与它的第一个生成器的类型是兼容的
for (i <- 0 to 1; c <- "hello") yield (c + i).toChar  //scala.collection.immutable.IndexedSeq[Char] = Vector(h, e, l, l, o, i, f, m, m, p)


函数
def abs(x: double) = {  // 需要给出参数的类型, 对递归函数, 需要给出返回值的类型, 像是这样: def fac(n: Int): Int = if (n <= 0) 1 else n * fac(n - 1)
    var r = 1
    r + 1  // 最后一个表达式的值是函数的返回值, 当然也可以写return
}

def default_p(str: String = "abc") = {
    str
}


变长参数
def sum(args: Int*) = {  // Int*定义了一个变长参数, 可以用sum(1, 2, 3)调用
    "sum"
}
如果传入的参数是序列, 要用如下的方式调用
val s = sum(1 to 5: _*)

指定返回值类型(Int)的函数
def abs:Int = 5

没有参数且不指定返回值类型的函数
def abs2 = 6

过程
过程不返回值, 只是为了得到过程中的某些效果, 比如打印
def box(s: String) {  // 没有等号, 就返回Unit, 即不返回值
    println("aaa")
}


懒值
当声明为懒值时, 初始化将被推迟, 直到首次对它取值, 常用在开销较大的打开文件等操作
lazy val s = "ss"


异常
if (x > 0) {
    sqrt(x)
} else {
    throw new IllegalArgumentException("abc")
}

try {
    "abc".toInt
} catch {
    case error: NumberFormatException => print("qwe")
}

try {
    "abc".toInt
} finally {
    print("qwe")
}

数组

定长数组
val nums = new Array[Int](10)  // 10个整数的数组, 元素初始化为0
val s = Array("hello", "world")  // 提供初始值就不需要new
s(0) = "googbye"  // 使用()访问元素


变长数组
import scala.collection.mutable.ArrayBuffer

val b = ArrayBuffer[Int]()  // 或者 new ArrayBuffer[Int] , 定义一个空的变长数组
b += 1  // 末尾添加一个元素
b += (1, 2, 3, 4)  // 末尾添加多个元素
b ++= Array(8, 13, 21)  // 追加一个集合
b.trimEnd(5)  // 移除最后的5个元素
b.insert(2, 6)  // 下标2之前插入
b.insert(2, 7, 8, 9)  //下标2之前插入多个元素
b.remove(2)  // 移除下标2的元素
b.remove(2, 3)  // 3的含义是从下标2开始移除3个元素

b.toArray  // 将变长数组转换为数组, 同理, toBuffer是将数组转换为变长数组


数组遍历
val s = Array("hello", "world")

for (i <- 0 until s.length) {
    println(i, s(i))
}

for (i <- 0 until (s.length, 2)) {  // 两个元素一跳
    println(i, s(i))
}

for (i <- (0 until s.length).reverse) {  // 从尾端开始遍历
    println(i, s(i))
}

for (elem <- s) {  // 不需要下标的话可以直接遍历
    println(elem)
}


数组转换
数组转换会生成新的数组, 类型和原来的数组类型一致(数组或变长数组)
val a = Array(1, 2, 3, 4, 5, 6)
for (elem <- a if elem % 2 == 0) yield {
    2 * elem
}


常用算法
Array(1, 2, 3).sum  // 求和
Array(1, 2, 3).max
Array(1, 2, 3).min

val a = Array(1, 4, 2, 6)
val bSorted = b.sorted(_ < _)  // 排序, 返回一个新的数组, 也支持sortWith定义排序方法

import scala.util.Sorting.quickSort
quickSort(a)  // 直接对数组排序, 但不能对数组缓冲排序

a.mkString(" and ")  // 指定分隔符, 显示数组内容
a.mkString("<", " and ", ">")  // 指定前缀和后缀


多维数组
val triangle = new Array[Array[Int]] (10)  // 外层数组长度为10
for (i <- 0 until triangle.length){
    triangle(i) = new Array[Int] (5)  // 内层数组长度为5
}

val matrix = Array.ofDim[Int] (3, 4)  // 简单创建三行四列的二维数组

映射

构造映射
val scores = Map("Alice" -> 10, "Bob" -> 3, "Cindy" -> 8)  //不可变映射,也可以写成 Map(("Alice", 10), ("Bob", 3), ("Cindy", 8)) 
val scores = scala.collection.mutable.Map("Alice" -> 10, "Bob" -> 3, "Cindy" -> 8)  //可变映射

val scores = new scala.collection.mutable.HashMap[String, Int]  // 定义一个空的映射


获取映射中的值
scores("Alice")  // 获取映射中的值
scores.contains("Alice")  // 映射是否包含某个键
scores.getOrElse("Alice", 0)  // 映射中若没有某个键则返回0


更新映射中的值
scores("ss") = 5  // 映射赋值
scores += ("Bob" -> 3, "Cindy" -> 8)
scores -= "Bob"

对于不可变的映射, 可以做如下操作, 使其返回一个新的映射, 效率不是很低, 他们共享大部分数据结构
val newScores = scores + ("Bob" -> 3, "Cindy" -> 8)
scores = scores + ("Bob" -> 3, "Cindy" -> 8)  // scores是由var 定义的
scores = scores - "Bob"


迭代映射
for ((k, v) <- scores) {
    println(k, v)
}

for (k <- scores.keySet) {  // 只访问键
    println(k)
}

for (k <- scores.values) {  // 只访问值
    println(k)
}

for ((k, v) <- scores) yield {  // 反转键值
    (v, k)
}


已排序映射
val scores = scala.collection.immutable.SortedMap("Cindy" -> 8)  // 平衡树实现的映射, 当所使用的键没有好的哈希函数的时候可以使用
val scores = scala.collection.mutable.LinkedHashMap("Cindy" -> 8)   // 有序的map


元组
元组是不同类型的值的集合, 可以用在函数需要返回不止一个值得时候
val t = (1, 3.14, "abc")  // 初始化
val second = t._2  // 访问第二个元素, 下标是从1开始的
val (first, second, third) = t


对偶数组
val symbols = Array("<", "-", ">")
val counts = Array(2, 10, 2)
val paris = symbols.zip(counts)  // Array[(String, Int)] = Array((<,2), (-,10), (>,2))

for ((s, n) <- paris) {  // 可以这样遍历对偶
    println(s, n)
}

简单类和无参方法
class Counter {
    private var value = 0

    def increment() {  // 方法默认是共有的
        value += 1
    }

    def current() = {
        value
    }
}

val myCounter = new Counter // 实例化类, 或写为 new Counter()
myCounter.increment()  // 对改值器(改变对象状态的方法), 调用的时候一般加上 ()
println(myCounter.current)  // 对取值器(不改变对象状态的方法), 调用的时候一般不加 ()


带getter和setter属性
scala对每个字段都提供getter和setter方法
class Person {
    var age = 0  // 和java或python不同, 这里定义出来就是一个私有字段
}
val fred = new Person
fred.age  // res30: Int = 0
fred.age = 21  // fred.age: Int = 21

可以自己定义getter和setter方法
class Person {
    private var privateAge = 0  // 加上private的话, 生成的getter和setter方法是私有的

    def age = privateAge
    def age_=(newValue: Int) {  // age_ 是age 的setter方法, 注意=左右不能有空格
        if (newValue > privateAge) {  // 不能变的年轻
            privateAge = newValue
        }
    }
}

val fred = new Person
fred.age  // res30: Int = 0
fred.age = 21  // fred.age: Int = 21
fred.age = 19  // fred.age: Int = 21


只有getter属性
class Person {
    val age = 0  // 以val定义的时候, 只会生成getter属性
}

有时候我们需要这样一个属性, 客户端不能更改, 但是可以被类本身修改
class Person {
    private var age = 0
    def currentAge = age  // 客户端不能访问age, 但是能访问currentAge
}


对象私有字段
class Person {
    private[this] var age = 0  // this指的是当前对象, 这时Person对象中的方法只能访问当前对象的age, 而不能访问其他Person对象中的age
}


Bean属性
scala.reflect.BeanProperty 可以使scala类生成符合java规范的getter和setter方法


辅助构造器
1. 辅助构造器名称为this
2. 每一个辅助构造器都必须以一个对先前已定义的辅助构造器或主构造器的调用开始
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 = new Person
val p2 = new Person("Tom")
val p3 = new Person("Tom", 12)


主构造器
class Person(val name: String, val age: Int = 0) {}  // 主构造器的定义放在类名之后

class Person(val name: String, val age: Int) {
    println("abc")  // 主构造器会执行类定义中的所有语句
}
针对主构造器参数生成的字段和方法
主构造器参数                          生成的字段和方法
name: String                            只有主构造器可以使用, 主构造器定义的变量才可以被对象访问
private val/var name: String            私有字段, 私有getter/setter
val/var name:String                     私有字段, 公有getter/setter
@BeanProperty val/var name:String       私有字段, 公有java版getter/setter


class Person(name: String) {
    val myname = name
}
val p = new Person("Tom")
p.name  // error: value name is not a member of Person
p.myname  // res10: String = Tom

class Person(val name: String) {}
val p = new Person("Tom")
p.name  // res11: String = Tom

对象

单例对象
对象的构造器在对象第一次使用的时候被调用
对象不能提供构造器参数
可用来存放工具函数或常量, 高效的共享某个不可变实例
object Accounts {
    private var lastNumber = 0
    def newUniqueNumber() = {lastNumber += 1; lastNumber}
}

Accounts.newUniqueNumber()


伴生对象
类和它的伴生对象可以相互访问私有特性, 它们必须存在于同一个源文件中
object Accounts {
    private var lastNumber = 0
    def newUniqueNumber() = {lastNumber += 1; lastNumber}
}

class Accounts {
    val id = Accounts.newUniqueNumber()
    private var balance = 0.0
    def deposit(amount: Double) {balance += amount}
}

var myAccount1 = new Accounts
myAccount1.id  // res2: Int = 1

var myAccount2 = new Accounts
myAccount2.id  // res1: Int = 2


扩展类或特质的对象
有点类似于继承...
一个有用的场景是给出可被共享的缺省对象
abstract class UndoableAction(val description: String) {
    def undo(): Unit  // def undo(): Unit = {Unit}  :后面接的是返回值类型
    def redo(): Unit
}

object DoNothingAction extends UndoableAction("Nothing") {
    override def undo() {}
    override def redo() {}
}


apply方法
当遇到如下形式的表达式时, apply方法会被调用
Object(parameter1, parameter2 ...)
通常, 这样一个apply方法返回的是伴生类的对象
比如Array对象的apply方法, 我们使用 Array("Mary", "Tom") 来创建一个数组, 而使用构造器创建(new Array(100))这种形式, 就会比较麻烦, 尤其是在创建多维数组的时候
注意: Array(100) 创建的是 [100] 这样一个数组, new Array(100) 创建的是包含100null元素的数组

class Account private (val id: Int, initialBalance: Double) {
    private var balance = initialBalance
}

object Account {
    def apply(id: Int, initialBalance: Double) = new Account(id, initialBalance)
}

val acct = Account(10, 100.0)


应用程序对象
每个scala都必须从一个对象的main方法开始
object Hello {
    def main(args: Array[String]) {
        println("hello")
    }
}

也可以通过App特质, 将程序代码放入一个构造器方法内
object Hello extends App {
    if (args.length > 0) {
        print("hello" + args(0))
    } else {
        println("hello")
    }
}


枚举
再议, 再议....

包和引入

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值