- Scalable 編程語言
- 純正的的面向對象語言
- 函數式編程語言
- 無縫的java互操作
scala之父 Martin Odersky
1. 函數式編程
- 函數式編程(functional programming) 或稱函數程序設計,又稱泛函編程,是一種編程范型
- 它將電腦運算視為數學上的函數計算,並且避免使用程序狀態以及易變對象。
- 函數編程語言最重要的基礎是λ演算(lambda calculus), 而且λ演算的函數可以接受函數當作輸入(引數)和輸出(傳出值)。
- 函數式編程強調程序執行的結果而非執行的過程,倡導利用若干簡單的執行單元讓計算結果不斷漸進,逐層推導復雜的運算,而不是設計一個復雜的執行過程。
1.1 函數式編程的重要概念
使用純函數
編程, 純函數是沒有副作用
的函數
1.副作用
所謂"副作用"(side effect),指的是函數內部與外部互動(最典型的情況,就是修改全局變量的值),產生運算以外的其他結果。
函數式編程強調沒有"副作用",意味着函數要保持獨立,所有功能就是返回一個新的值,沒有其他行為,尤其是不得修改外部變量的值。
var x = 1
def XplusY_v1(y: Int) = x+y
def XplusY_v2(y: Int) = {x = x+y; x}
這里 XplusY_v2對 x 就有副作用, 函數執行之后 x 的狀態會發生變化
而 XplusY_v1 執行之后不會改變 x 的值, XplusY_v1是純函數
2.引用透明性
定義: 如果f(x)的參數和函數體都是引用透明的, 那么函數f是純函數
特點: 對於相同的輸入, 總是得到相同的輸出.就是說,表達式的值不依賴於值可以改變
的全局狀態。
var x = new StringBuild("Hello")
var y = x.append("world")
var z = x.append("world")
上面形同的表達式x.append("world")得到的結果不同, 由於x是一個值可以改變的變量
3.不變性(Immutablity)
為了獲得透明性, 任何值都不能改變
4.函數是一等公民
(First-class Function)
函數與其他數據類型一樣,處於平等地位,可以賦值給其他變量,也可以作為參數,傳入另一個函數,或者作為別的函數的返回值。
5.只有表達式
一切都是計算, 函數式編程中只有表達式, 變量, 函數都是表達式
"表達式"(expression)是一個單純的運算過程,總是有返回值;"語句"(statement)是執行某種操作,沒有返回值。也就是說,每一步都是單純的運算,而且都有返回值。
6.閉包和高階函數
函數編程支持函數作為第一類對象,有時稱為 閉包或者 仿函數(functor)對象。實質上,閉包是起函數的作用並可以像對象一樣操作的對象。
高階函數(Higher order Function)可以用另一個函數(間接地,用一個表達式) 作為其輸入參數,在某些情況下,它甚至返回一個函數作為其輸出參數。
7.表達式求值策略
- 嚴格求值
- 非嚴格求值
- 惰性求值
8.遞歸函數
遞歸實現循環: 尾遞歸
1.2 函數式編程的優勢
- 生產效率高
- 易於推理
- 並行計算
- 多核計算
2. MacOS安裝scala開發環境
3步走:
- 安裝scala
brew install scala
- 安裝sbt
brew install sbt
- Intellj IDEA上scala ,sbt插件下載
3. Scala 類型體系

4. Scala求值策略
Scala有兩種求值策略
- Call By Value 對函數實參求值,且只求一次,如果作為參數值需要對表達式先進行求值
- Call By Name 對函數實參每次在函數體內被調用時都會求值
通常使用Call By Value
如果函數形參以 =>
開頭,那么會使用Call By Name
def foo(x: Int) = x // call by value
def foo(x: => Int) = x //call by name
例子
def test1(x: Int, y: Int): Int = x * x
def test2(x: => Int, y: => Int): Int = x * x
計算過程:
test1(3+4, 8) | test2(3+4, 8) | test1(7, 2 *4) | test2(7, 2 *4)
test1(7, 8) | (3+4)*(3+4) | test1(7, 8) | 7*7
7*7 | 7*(3+4) | 7*7 | 49
49 | 7*7 | 49 |
| 49 | |
def bar1(x: Int, y: =>Int) = 1
def bar2(x: => Int, y: =>Int) = 1
def loop(): Int = loop
bar1(loop, 1) 死循環, 這里實參一直帶求值loop, 一直沒法拿到值
bar2(loop, 1) 表達式值為1
5. Scala函數
scala中函數是第一等公民:
- 把函數作為實參傳給另一個函數
- 把函數作為返回值
- 把函數賦值給變量
- 把函數存儲在數據結構中
在Scala中函數和普通變量一樣, 同樣也具有函數類型
5.1 函數類型
在Scala中, 函數類型格式為 A => B
表示一個接受類型A的參數, 並返回類型B的參數, 如 Int => String
5.2 高階函數
用函數作為形參或者返回值的函數, 稱為高階函數
def operate(f: (int, int) => Int) = {
f(4, 4)
}
def greeting() = (name: String) => {"hello" + " " + name} // 匿名函數
5.3 匿名函數
匿名函數就是函數常量, 也稱為函數文字量
scala中匿名函數的格式為:
(形參列表) => (函數體)
scala> (x: Int, y: Int) => x+y
res2: (Int, Int) => Int = $$Lambda$1209/886693791@35c8be21
scala> res2(1, 4)
res3: Int = 5
5.4 柯里化函數(curried function)
柯里化函數把具有多個參數的函數轉換成一條函數鏈, 每個節點上是單一參數
下面兩個例子的函數定義是等價的:
def add(x: Int, y: Int) = x + y
def add(x: Int)(y: Int) = x + y //scala里柯里化的語法
def curriedAdd(x: Int)(y: Int) = x + y
curriedAdd(1)(2) //4
val addOne = curriedAdd(1)_ // Int => Int
addOne(2) //3
5.5 遞歸函數
遞歸函數是函數式編程中實現循環的一種技術
def factorial(n: Int): Int = {
if (n == 0) 1 else n * factorial(n-1)
}
尾遞歸
@annotation.tailrec
def factorial(n: Int, m: Int): Int = {
if (n == 0) m else factorial(n-1, n * m)
}
@annotation.tailrec 告訴 scala使用尾遞歸, 這里內存中開辟一個棧用來存儲m
的值, 而不是n個棧來存儲計算中間結果
5.6 例子
def sum(f: Int => Int)(a: Int)(b: Int): Int = {
@annotation.tailrec
def loop(n: Int, acc: Int): Int = {
if (n > b) acc
else loop(n + 1, acc + f(n))
}
loop(a, 0)
}
sum(x => x)(1)(3) // 6
sum(x => x * x)(1)(3) //14
val s1 = sum(x => x * x)_ // 偏函數, _ 通配符用來匹配后面的參數
s1(1)(3) //14
說明:
1.上面是個科里化函數, 有三個形式參數
- 函數f, 函數類型 Int > Int
- 形參a, 類型Int
- 形參b, 類型Int
2.函數中定義了一個尾遞歸函數 loop, 實現相加
3.sum(x => x), 其中 x => x 是一個匿名函數
6. Scala 集合
scala.collection.immutable

6.1 List[T]
T 表示泛型
// :: 連接操作符
val a = List(1, 2, 3, 4)
val b = 0 :: a
//a: List[Int] = List(1, 2, 3, 4)
//b: List[Int] = List(0, 1, 2, 3, 4)
// Nil表示一個空列表
// 先 z 連接 Nil, 然后 y 連接 z ...
val c = "x" :: "y" :: "z" :: Nil
//c: List[String] = List(x, y, z)
// Any 類型
val d = a ::: c
//d: List[Any] = List(1, 2, 3, 4, x, y, z)
// 返回List的第一個元素
a.head
c.head
d.head
//res0: Int = 1
//res1: String = x
//res2: Any = 1
// 返回除了第一個元素之外元素組成的 List
a.tail
c.tail
d.tail
//res3: List[Int] = List(2, 3, 4)
//res4: List[String] = List(y, z)
//res5: List[Any] = List(2, 3, 4, x, y, z)
a.isEmpty
Nil.isEmpty
//res6: Boolean = false
//res7: Boolean = true
def walkthru(l : List[Int]): String = {
if (l.isEmpty) ""
else l.head.toString + walkthru(l.tail)
}
walkthru(List(1, 3, 5, 7))
//walkthru: walkthru[](val l: List[Int]) => String
//res8: String = 1357
其他方法
a.filter(x => x%2 == 1)
//res9: List[Int] = List(1, 3)
"hello life".toList
//res10: List[Char] = List(h, e, l, l, o, , l, i, f, e)
"hel 89 life".toList.filter(x => Character.isDigit(x))
//res11: List[Char] = List(8, 9)
"hel 89 life".toList.takeWhile(x=>x!='9')
//res12: List[Char] = List(h, e, l, , 8)
// _ 通配符
c
c.map(x => x.toUpperCase)
c.map(_.toLowerCase)
//res13: List[String] = List(x, y, z)
//res14: List[String] = List(X, Y, Z)
//res15: List[String] = List(x, y, z)
a
a.filter(_ % 2 == 1).map(_ + 10)
//res16: List[Int] = List(1, 2, 3, 4)
//res17: List[Int] = List(11, 13)
val q = List(a, List(5, 6, 7))
q.map(_.filter(_%2 == 0))
q.flatMap(_.filter(_%2 == 0))
//q: List[List[Int]] = List(List(1, 2, 3, 4), List(5, 6, 7))
//res18: List[List[Int]] = List(List(2, 4), List(6))
//res19: List[Int] = List(2, 4, 6)
6.2 歸約操作
6.2.1 reduceleft
reduceLeft(op: (T,T) => T)

a
a.reduceLeft((x, y) => x+y)
a.reduce(_ + _)
// res20: List[Int] = List(1, 2, 3, 4)
// res21: Int = 10
// res22: Int = 10
6.2.2 foldLeft
foldLeft(z: U)(op: (U, T) => U)
a.foldLeft(1)(_ + _)
a
a.foldLeft(1)(_ + _)
// res20: List[Int] = List(1, 2, 3, 4)
// res23: Int = 11

7. Range & Stream & tuple & Map
7.1 Range & Stream
1 to 4
(1 to 7 by 2).toList
1 until 6
//res24: scala.collection.immutable.Range.Inclusive = Range 1 to 4
// res25: List[Int] = List(1, 3, 5, 7)
// res26: scala.collection.immutable.Range = Range 1 until 6
// Stream 惰性求值
1 #:: 2 #:: 3 #:: Stream.empty
//scala.collection.immutable.Stream[Int] = Stream(1, ?)
val stream = (1 to 1000000).toStream
//res28: scala.collection.immutable.Stream[Int] = Stream(1,?)
stream.tail
stream.head
//res28: scala.collection.immutable.Stream[Int] = Stream(2, ?)
//res29: Int = 1
7.2 tuple
(1, 2)
val tuple1 = (3, "hello", (1, "we"))
tuple1._1
tuple1._3._1
// res30: (Int, Int) = (1,2)
// tuple1: (Int, String, (Int, String)) = (3,hello,(1,we))
// res31: Int = 3
// res32: Int = 1
def sumSq(in: List[Int]): (Int, Int, Int) = {
in.foldLeft((0,0,0))((t,v) => (t._1 + 1, t._2 + v, t._3 + v*v))
}
a
sumSq(a)
//sumSq: sumSq[](val in: List[Int]) => (Int, Int, Int)
// res33: List[Int] = List(1, 2, 3, 4)
// res34: (Int, Int, Int) = (4,10,30)
7.3 Map(K, V)
// 鍵的類型要一致, 值得類型可以不一致
val map = Map(1 -> "david", 9 -> "ds")
map(9)
map.contains(1)
map.contains(2)
//scala.collection.immutable.Map[Int,String] = Map(1 -> david, 9 -> ds)
// String = ds
// Boolean = true
// Boolean = false
map.keys
map.values
// 下列操作map自身是不變的
map + (8 -> 3)
map
map - 1
map
//scala.collection.immutable.Map[Int,Any] = Map(1 -> david, 9 -> ds, 8 -> 3)
// Map(1 -> david, 9 -> ds)
// scala.collection.immutable.Map[Int,String] = Map(9 -> ds)
// scala.collection.immutable.Map[Int,String] = Map(1 -> david, 9 -> ds)
// 添加多個元素, 如果添加的key已經存在, 則更新
map ++ List(2 -> "Alice", 1-> "Hello")
//Map(1 -> Hello, 9 -> ds, 2 -> Alice)
map -- List(2, 1, 9)
// Map()
// 如果key不存在則忽略
7.4 快排
def qSort(a: List[Int]): List[Int] = {
if (a.length < 2) a
else qSort(a.filter(a.head > _)) ++
a.filter(a.head == _) ++
qSort(a.filter(a.head < _))
}
qSort(List(1, 4, 2, 3, 9, 5))
//res46: List[Int] = List(1, 2, 3, 4, 5, 9)