国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

快學(xué)Scala學(xué)習(xí)筆記及習(xí)題解答(12-14高階函數(shù)、集合、模式匹配和樣例類)

2019-11-14 10:20:46
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

本文Scala使用的版本是2.11.8

第12章 高階函數(shù)

12.1 基本用法

作為值得函數(shù)

import scala.math._val num = 3.14// ceil函數(shù)后的_表示這是個(gè)函數(shù),而不是忘記傳參val fun = ceil _// 調(diào)用fun(num)// 傳遞Array(3.14, 1.42, 2.0).map(fun)

匿名函數(shù)

// 存放到變量val triple = (x: Double) => 3 * x// 傳遞給另一個(gè)函數(shù)Array(3.14, 1.42, 2.0).map((x: Double) => 3 * x)

定義接收函數(shù)參數(shù)的函數(shù)

def valueAtOneQuarter(f: (Double) => Double) = f(0.25)// 類型為((Double) => Double) => Double// 也可以產(chǎn)生另一個(gè)函數(shù)def mulBy(factor: Double) = (x: Double) => factor * x// 類型為(Double) => ((Double) => Double)

參數(shù)類型推斷

Scala會(huì)盡可能幫助推斷類型信息。例如,如果參數(shù)在=>右側(cè)只出現(xiàn)一次,可以用_替換它。

valueAtOneQuarter(3 * _)

一些有用的高階函數(shù)

函數(shù)名 描述
map 將一個(gè)函數(shù)應(yīng)用到某個(gè)集合的所有元素并返回結(jié)果
foreach 將函數(shù)應(yīng)用到每個(gè)元素,無(wú)返回值
filter 輸出所有匹配某個(gè)特定條件的元素
reduceLeft 接受一個(gè)二元的函數(shù),并將它應(yīng)用到序列中的所有元素,從左到右
sortWith 接受一個(gè)二元函數(shù),進(jìn)行排序

閉包

def mulBy(factor: Double) = (x: Double) => factor * xval triple = mulBy(3)val half = mulBy(0.5)PRintln(triple(14) + " " + half(14)) // 將打印42 7

每一個(gè)返回的函數(shù)都有自己的factor設(shè)置。這樣的函數(shù)被稱作閉包(closure)。閉包由代碼和代碼用到的任何非局部變量定義構(gòu)成。

這些函數(shù)實(shí)際上是以類的對(duì)象方式實(shí)現(xiàn)的。

12.2 SAM轉(zhuǎn)換

在Scala中,每當(dāng)想要告訴另一個(gè)函數(shù)做某件事時(shí),會(huì)傳一個(gè)函數(shù)參數(shù)給它。而java是將動(dòng)作放在一個(gè)實(shí)現(xiàn)某接口的類中,然后將該類的一個(gè)實(shí)例傳遞給另一個(gè)方法。

這些接口被稱做SAM類型(single abstract method)。

Java實(shí)現(xiàn)

import java.awt.event.{ActionEvent, ActionListener}import javax.swing.{JButton, JFrame}object Note1 { implicit def makeAction(action: (ActionEvent) => Unit) = new ActionListener { override def actionPerformed(event: ActionEvent) { action(event) } } def main(args: Array[String]) { var data = 0 val frame = new JFrame("SAM Testing"); val jButton = new JButton("Counter") jButton.addActionListener(new ActionListener { override def actionPerformed(event: ActionEvent) { data += 1 println(data) } }) }}

Scala實(shí)現(xiàn)

import java.awt.event.{ActionEvent, ActionListener}import javax.swing.{JButton, JFrame}object Note1 { // 定義隱式轉(zhuǎn)換,把函數(shù)轉(zhuǎn)換成一個(gè)ActionListener實(shí)例 implicit def makeAction(action: (ActionEvent) => Unit) = new ActionListener { override def actionPerformed(event: ActionEvent) { action(event) } } def main(args: Array[String]) { var data = 0 val frame = new JFrame("SAM Testing"); val jButton = new JButton("Counter") // 傳遞函數(shù)參數(shù) jButton.addActionListener((event: ActionEvent) => { data += 1; println(data) }) frame.setContentPane(jButton); frame.pack(); frame.setVisible(true); }}

12.3 柯里化

柯里化(currying)指的是將原來(lái)接受兩個(gè)參數(shù)的函數(shù)變成新的接受一個(gè)參數(shù)的函數(shù)的過(guò)程。新的函數(shù)返回一個(gè)以原有第二個(gè)參數(shù)做為參數(shù)的函數(shù)。

def mulOneAtATime(x: Int) = (y: Int) => x * y// 調(diào)用mulOneAtATime(6)(7)// Scala支持如下簡(jiǎn)寫來(lái)定義這樣的柯里化函數(shù)def mulOneAtATime(x: Int)(y: Int) = x * y

示例

val a = Array("Hello", "World")val b = Array("hello", "world")a.corresponds(b)(_.equalsIgnoreCase(_))

12.4 控制抽象

/** * 如下: runInThread2方式定義函數(shù)就比runInThread要優(yōu)雅很多 * 調(diào)用時(shí), 不用輸入 () => */object Note2 { def runInThread(block: () => Unit): Unit = { new Thread { override def run() { block() } }.start() } def runInThread2(block: => Unit): Unit = { new Thread { override def run() { block } }.start() } def main(args: Array[String]) { runInThread { () => println("Hi"); Thread.sleep(10000); println("Bye") } runInThread2 { println("Hi"); Thread.sleep(10000); println("Bye") } }}

通過(guò)上面這樣的應(yīng)用,可以構(gòu)建控制抽象:看上去像是編程語(yǔ)言的關(guān)鍵字的函數(shù)。

示例:實(shí)現(xiàn)像while語(yǔ)句的函數(shù)

object Note2 { // 函數(shù)參數(shù)的專業(yè)術(shù)語(yǔ)叫換名調(diào)用參數(shù), 和常規(guī)的參數(shù)不同, 函數(shù)在被調(diào)用時(shí), // 參數(shù)表達(dá)式不會(huì)被求值, 如下 x == 0 def until(condition: => Boolean) (block: => Unit): Unit = { if (!condition) { block until(condition)(block) } } def main(args: Array[String]): Unit = { var x = 10 until (x == 0) { x -= 1 println(x) } }}

12.5 return表達(dá)式

object Note2 { // 函數(shù)參數(shù)的專業(yè)術(shù)語(yǔ)叫換名調(diào)用參數(shù), 和常規(guī)的參數(shù)不同, 函數(shù)在被調(diào)用時(shí), // 參數(shù)表達(dá)式不會(huì)被求值, 如下 x == 0 def until(condition: => Boolean)(block: => Unit): Unit = { if (!condition) { block until(condition)(block) } } // 如果在帶名函數(shù)中使用return的話, 則需要給出其返回類型 def indexOf(str: String, ch: Char): Int = { var i = 0 until(i == str.length) { if (str(i) == ch) return i i += 1 } -1 } def main(args: Array[String]): Unit = { println(indexOf("test", 'x')) }}

12.6 習(xí)題解答

1. 編寫函數(shù)values(fun: (Int) => Int, low: Int, high: Int),該函數(shù)輸出一個(gè)集合,對(duì)應(yīng)給定區(qū)間內(nèi)給定函數(shù)的輸入和輸出。比如,values(x => x * x, -5, 5)應(yīng)該產(chǎn)出一個(gè)對(duì)偶的集合(-5, 25), (-4, 16), (-3, 9), …, (5, 25)

object One { def values(fun: (Int) => Int, low: Int, high: Int) = { var array = List[(Int, Int)]() low to high foreach { x => array = array :+ (x, fun(x)) } array } def main(args: Array[String]) { println(values(x => x * x, -5, 5).mkString(" ")) }}// 結(jié)果(-5,25) (-4,16) (-3,9) (-2,4) (-1,1) (0,0) (1,1) (2,4) (3,9) (4,16) (5,25)

2. 如何用reduceLeft得到數(shù)組中的最大元素?

object Two { def main(args: Array[String]) { val arr = Array(1, 333, 4, 6, 4, 4, 9, 32, 6, 9, 0, 2) val max = arr.reduceLeft((x, y) => { if (x > y) x else y }) println(max) }}// 結(jié)果333

3. 用to和reduceLeft實(shí)現(xiàn)階乘函數(shù),不得使用循環(huán)或遞歸

object Three { def factorial(n: Int): Int = { if (n > 0) { 1 to n reduceLeft (_ * _) } else if (n == 0) { 1 } else { throw new IllegalArgumentException("請(qǐng)輸入非負(fù)數(shù).") } } def main(args: Array[String]) { println(factorial(-1)) }}// 結(jié)果120

4. 前一個(gè)實(shí)現(xiàn)需要處理一個(gè)特殊情況,即n<1的情況。展示如何用foldLeft來(lái)避免這個(gè)需要。(在scaladoc中查找foldLeft的說(shuō)明。它和reduceLeft很像,只不過(guò)所有需要結(jié)合在一起的這些值的首值在調(diào)用的時(shí)候給出。)

object Four { def factorial(n: Int): Int = { if (n < 0) { throw new IllegalArgumentException("請(qǐng)輸入非負(fù)數(shù).") } else { (1 to n).foldLeft(1)(_ * _) } } def main(args: Array[String]) { println(factorial(0)) }}

5. 編寫函數(shù)largest(fun: (Int) => Int, inputs: Seq[Int]),輸出在給定輸入序列中給定函數(shù)的最大值。舉例來(lái)說(shuō),largest(x => 10 * x - x * x, 1 to 10)應(yīng)該返回25。不得使用循環(huán)或遞歸

object Five { def largest(fun: (Int) => Int, inputs: Seq[Int]): Int = { inputs.map(fun).max } def main(args: Array[String]) { println(largest(x => 10 * x - x * x, 1 to 10)) }}

6. 修改前一個(gè)函數(shù),返回最大的輸出對(duì)應(yīng)的輸入。舉例來(lái)說(shuō),largestAt(fun: (Int) => Int, inputs: Seq[Int])應(yīng)該返回5。不得使用循環(huán)或遞歸

object Six { def largestAt(fun: (Int) => Int, inputs: Seq[Int]): Int = { inputs.map(x => (x, fun(x))) .reduceLeft((x, y) => if (x._2 > y._2) x else y)._1 } def main(args: Array[String]) { println(largestAt(x => 10 * x - x * x, 1 to 10)) }}

7. 要得到一個(gè)序列的對(duì)偶很容易,比如: val pairs = (1 to 10) zip (11 to 20) 假定你想要對(duì)這個(gè)序列做某種操作,比如,給對(duì)偶中的值求和,但是你不能直接使用: pairs.map( + ) 函數(shù) _ + _ 接受兩個(gè)Int作為參數(shù),而不是(Int, Int)對(duì)偶。編寫函數(shù)adjustToPair,該函數(shù)接受一個(gè)類型為(Int, Int) => Int的函數(shù)作為參數(shù),并返回一個(gè)等效的, 可以以對(duì)偶作為參數(shù)的函數(shù)。舉例來(lái)說(shuō)就是:adjustToPair(_ * _)((6, 7))應(yīng)得到42。然后用這個(gè)函數(shù)通過(guò)map計(jì)算出各個(gè)對(duì)偶的元素之和

object Seven { def ajustToPair(fun: (Int, Int) => Int) = (x: (Int, Int)) => fun(x._1, x._2) def main(args: Array[String]) { val x = ajustToPair(_ * _)((6, 7)) println(x) val pairs = (1 to 10) zip (11 to 20) println(pairs) val y = pairs.map(ajustToPair(_ + _)) println(y) }}// 結(jié)果42Vector((1,11), (2,12), (3,13), (4,14), (5,15), (6,16), (7,17), (8,18), (9,19), (10,20))Vector(12, 14, 16, 18, 20, 22, 24, 26, 28, 30)

8. 在12.8節(jié)中,你看到了用于兩組字符串?dāng)?shù)組的corresponds方法。做出一個(gè)對(duì)該方法的調(diào)用,讓它幫我們判斷某個(gè)字符串?dāng)?shù)組里的所有元素的長(zhǎng)度是否和某個(gè)給定的整數(shù)數(shù)組相對(duì)應(yīng)

object Eight { def main(args: Array[String]) { val a = Array("asd", "df", "abcd") val b = Array(3, 2, 4) val c = Array(3, 2, 1) println(a.corresponds(b)(_.length == _)) println(a.corresponds(c)(_.length == _)) }}// 結(jié)果truefalse

9. 不使用柯里化實(shí)現(xiàn)corresponds。然后嘗試從前一個(gè)練習(xí)的代碼來(lái)調(diào)用。你遇到了什么問(wèn)題?

沒(méi)有柯里化則不能使用前一個(gè)練習(xí)里的代碼方式來(lái)調(diào)用

10. 實(shí)現(xiàn)一個(gè)unless控制抽象,工作機(jī)制類似if,但條件是反過(guò)來(lái)的。第一個(gè)參數(shù)需要是換名調(diào)用的參數(shù)嗎?你需要柯里化嗎?

// 需要換名和柯里化object Ten { def unless(condition: => Boolean)(block: => Unit) { if (!condition) { block } } def main(args: Array[String]) { unless(0 > 1) { println("Unless!") } }}

第13章 集合

13.1 主要概念

所有的集合都擴(kuò)展自Iterable。有三類集合,分別是Seq、Set和Map。

Iterable提供了遍歷一個(gè)集合的基本方式。

每個(gè)集合特質(zhì)或類都有一個(gè)帶有apply方法的伴生對(duì)象,可以用于構(gòu)建該集合中的實(shí)例。

Seq

Seq是一個(gè)有先后次序的序列,比如數(shù)組和列表。IndexedSeq是它的子特質(zhì),允許通過(guò)下標(biāo)訪問(wèn)元素。

Set

Set是沒(méi)有次序的一組值,子特質(zhì)SortedSet的元素則以某種排過(guò)序的順序被訪問(wèn)。

Map

Map是一組對(duì)偶。SortedMap按照鍵的排序訪問(wèn)其中的實(shí)體。

可變和不可變集合

不可變的集合從不改變,因此可以安全地共享其引用。

13.2 序列

不可變序列

不可變序列

Vector是ArrayBuffer的不可變版本:可下標(biāo)訪問(wèn),支持快速隨機(jī)訪問(wèn)。它是以樹形結(jié)構(gòu)的形式實(shí)現(xiàn)的,每個(gè)節(jié)點(diǎn)可以有不超過(guò)32個(gè)子節(jié)點(diǎn)。

Range表示一個(gè)整數(shù)序列,它并不存儲(chǔ)所有值,而只是起始值、結(jié)束值和增值。

可變序列

可變序列

13.3 列表

不可變列表

列表要么是Nil(即空表),要么是一個(gè)head元素加上一個(gè)tail,而tail又是一個(gè)列表。

val digits = List(4, 2)// digits.head值是4,digits.tail是List(2)。// digits.tail.head是2,而digits.tail.tail是Nil

::操作符從給定的頭和尾創(chuàng)建一個(gè)新的列表

9 :: List(4, 2)// 返回 List(9, 4, 2)// 也可以如下(::是右結(jié)合的)9 :: 4 :: 2 :: Nil

列表遍歷,可以使用迭代器、遞歸或者模式匹配。下面是模式匹配示例:

def sum(lst: List[Int]): Int = lst match { case Nil => 0 case h :: t => h + sum(t)}

可變列表

可變的LinkedList和不可變List相似。不過(guò),可以通過(guò)對(duì)elem引用賦值來(lái)修改其頭部,對(duì)next引用賦值修改其尾部。

還提供了DoubleLinkedList,區(qū)別是多帶一個(gè)prev引用。

如果要把某個(gè)節(jié)點(diǎn)變成列表中的最后一個(gè)節(jié)點(diǎn),不能將next引用設(shè)為Nil,而是設(shè)置為L(zhǎng)inkedList.empty

13.4 集

集市不重復(fù)元素的集合。集并不保留元素插入的順序,缺省以哈希集實(shí)現(xiàn)的。

鏈?zhǔn)焦<梢杂涀≡乇徊迦氲捻樞颉?/p>val weekdays = scala.collection.mutable.LinkedHashSet("Mo", "Tu", "We", "Th", "Fr")

已排序的集(用紅黑樹實(shí)現(xiàn)):

scala.collection.immutable.SortedSet(1, 2, 3, 4, 5)

位集(BitSet)是以一個(gè)字位序列的方式存放非負(fù)整數(shù)。如果集中有i,則第i個(gè)字位是1。提供了可變和不可變的實(shí)現(xiàn)。

contains方法檢查某個(gè)集是否包含給定的值。subsetOf方法檢查某個(gè)集是否是另一個(gè)集的子集。

val digits = Set(1, 7, 2, 9)digits contains 0 // falseSet(1, 2) subsetOf digits // true

union、intersect和diff方法執(zhí)行通常的集操作。也可以寫作|、&&~。還可以將聯(lián)合(union)寫作++,將差異(diff)寫作--

val primes = Set(2, 3, 5, 7)digits union primes 等于 Set(1, 2, 3, 5, 7, 9)digits & primes 等于 Set(2, 7)digits -- primes 等于 Set(1, 9)

13.5 添加和去除元素的操作符

操作符 描述 集合類型
coll :+ elem elem +: coll 有elem被追加到尾部或頭部的與coll類型相同的集合 Seq
coll + elem coll + (e1, e2, …) 添加了給定元素的與coll類型相同的集合 Set、Map
coll - elem coll - (e1, e2, …) 給定元素被移除的與coll類型相同的集合 Set、Map、ArrayBuffer
coll ++ coll2 coll2 ++: coll 與coll類型相同的集合,包含了兩個(gè)集合的元素 Iterable
coll – coll2 移除了coll2中元素的與coll類型相同的集合(用diff來(lái)處理序列) Set、Map、ArrayBuffer
elem :: lst lst2 ::: lst 被向前追加了元素或給定列表的列表。和+:以及++:的作用相同 List
list ::: list2 等同于list ++: list2 List
set | set2 set & set2 set &~ set2 并集、交集和兩個(gè)集的差異。|等于++,&~等于– Set
coll += elem coll += (e1, e2, …) coll ++= coll2 coll -= elem coll -= (e1, e2, …) coll –= coll2 通過(guò)添加或移除給定元素來(lái)修改coll 可變集合
elem +=: coll coll2 ++=: coll 通過(guò)向前追加給定元素或集合來(lái)修改coll ArrayBuffer

一般,+用于將元素添加到無(wú)先后次序的集合,而+:和:+則是將元素添加到有先后次序的集合的開頭或末尾。

Vector(1, 2, 3) :+ 5 // Vector(1, 2, 3, 5)1 +: Vector(1, 2, 3) // Vector(1, 1, 2, 3)

和其他以冒號(hào)結(jié)尾的操作符一樣,+:是右結(jié)合的。

提示匯總?cè)缦?/strong>

向后(:+)或向前(+:)追加元素到序列當(dāng)中。添加(+)元素到無(wú)先后次序的集合中。用-移除元素。用++和–來(lái)批量添加和移除元素。對(duì)于列表,優(yōu)先使用::和:::。改值操作用+=、++=、-=和–=。對(duì)于集合,更喜歡++、&和–。盡量不用++:、+=:和++=:。對(duì)于列表可以用+:而不是::來(lái)保持與其他集合操作的一致性。但有一個(gè)例外:模式匹配(case h::t)不認(rèn)+:操作符。

13.6 常用方法

Iterable特質(zhì)

方法 描述
head、last、headOption、lastOption 返回第一個(gè)或最后一個(gè)元素,或者以O(shè)ption返回
tail、init 返回除第一個(gè)或最后一個(gè)元素外其余部分
length、isEmpty 返回集合長(zhǎng)度;或者,當(dāng)長(zhǎng)度為零時(shí)返回true
map(f)、foreach(f)、flatMap(f)、collect(pf) 將函數(shù)用到所有元素
reduceLeft(op)、reduceRight(op)、foldLeft(init)(op)、foldRight(init)(op) 以給定順序?qū)⒍僮鲬?yīng)用到所有元素
reduce(op)、fold(init)(op)、aggregate(init)(op, combineOp) 以非特定順序?qū)⒍僮鲬?yīng)用到所有元素
sum、product、max、min 返回和或乘積(前提元素類型可被隱式轉(zhuǎn)換為Numeric特質(zhì));或者最大值、最小值(前提可隱式轉(zhuǎn)換成Ordered特質(zhì))
count(pred)、forall(pred)、exists(pred) 返回滿足前提表達(dá)式的元素計(jì)數(shù);所有元素都滿足時(shí)返回true;或者至少有一個(gè)元素滿足時(shí)返回true。
filter(pred)、filterNot(pred)、partition(pred) 返回所有滿足前提表達(dá)式的元素;所有不滿足的元素;或者,這兩組元素組成的對(duì)偶
takeWhile(pred)、dropWhile(pred)、span(pred) 返回滿足前提表達(dá)式的一組元素(直到遇到第一個(gè)不滿足的元素);所有其他元素;或者,這兩組元素組成的對(duì)偶
take(n)、drop(n)、splitAt(n) 返回頭n個(gè)元素;所有其他元素;或者,這兩組元素組成的對(duì)偶
takeRight(n)、dropRight(n) 返回最后n個(gè)元素,或者所有其他元素
slice(from, to) 返回從from到to這個(gè)區(qū)間內(nèi)的所有元素
zip(coll2)、zipAll(coll2, fill, fill2)、zipWithIndex 返回由本集合元素和另一個(gè)集合的元素組成的對(duì)偶
grouped(n)、sliding(n) 返回長(zhǎng)度為n的子集迭代器;grouped產(chǎn)出下標(biāo)為0 until n的元素,然后是下標(biāo)為n until 2 * n的元素,以此類推;sliding產(chǎn)出下標(biāo)為0 until n的元素,然后是下標(biāo)為1 until n + 1的元素,以此類推
mkString(before, between, after)、addString(sb, before, between, after) 做出一個(gè)有所有元素組成的字符串,將給定字符串分別添加到首個(gè)元素之前、每個(gè)元素之間,以及最后一個(gè)元素之后。第二個(gè)方法將該字符串追加到字符串構(gòu)建器當(dāng)中
toIterable、toSeq、toIndexedSeq、toArray、toList、toStream、toSet、toMap 將集合轉(zhuǎn)換成指定類型集合
copyToArray(arr)、copyToArray(arr, start, length)、copyToBuffer(buf) 將元素拷貝到數(shù)組或緩沖當(dāng)中

Seq特質(zhì)

方法 描述
contains(elem)、containsSlice(seq)、startsWith(seq)、endsWith(seq) 返回true,如果該序列:包含給定元素;包含給定序列;以給定序列開始;或者,以給定序列結(jié)束
indexOf(elem)、lastIndexOf(elem)、indexOfSlice(seq)、lastIndexOfSlice(seq) 返回給定元素或序列在當(dāng)前序列中的首次或末次出現(xiàn)的下標(biāo)
indexWhere(pred) 返回滿足pred的首個(gè)元素的下標(biāo)
prefixLength(pred)、segmentLength(pred, n) 返回滿足pred的最長(zhǎng)元素序列的長(zhǎng)度,從當(dāng)前序列的下標(biāo)0或n開始查找
padTo(n, fill) 返回當(dāng)前序列的一個(gè)拷貝,將fill的內(nèi)容向后追加,直到新序列長(zhǎng)度達(dá)到n
intersect(seq)、diff(seq) 返回交集;或者差值
reverse 當(dāng)前序列反向
sorted、sortWith(less)、sortBy(f) 使用元素本身的大小、二元函數(shù)less,或者將每個(gè)元素映射成一個(gè)帶先后次序的類型的值得函數(shù)f,對(duì)當(dāng)前序列進(jìn)行排序后的新序列
premutations、combinations(n) 返回一個(gè)遍歷所有排列或組合(長(zhǎng)度為n的子序列)的迭代器

13.7 將函數(shù)映射到集合

val names = List("Peter", "Paul", "Mary")names.map(_.toUpperCase) // 結(jié)果:List("PETER", "PAUL", "MARY")// 等效于以下操作for (n <- names) yield n.toUpperCase// 將所有值連接到一塊def ulcase(s: String) = Vector(s.toUpperCase(), s.toLowerCase())names.map(ulcase)// 得到 List(Vector("PETER", "peter"), Vector("PAUL", "paul"), Vector("MARY", "mary"))names.flatMap(ulcase)// 得到 List("PETER", "peter", "PAUL", "paul", "MARY", "mary")

collect方法用于偏函數(shù)(partial function)

"-3+4".collect { case '+' => 1; case '-' => -1 }// 得到 Vector(-1, 1)

foreach

names.foreach(println)

13.8 化簡(jiǎn)、折疊和掃描

主要說(shuō)明方法用二元函數(shù)來(lái)組合集合中的元素。

List(1, 7, 3, 9).reduceLeft(_ - _)// 相當(dāng)于 ((1 - 7) - 2) - 9 = -17List(1, 7, 3, 9).reduceRight(_ - _)// 相當(dāng)于 1 - (7 - (2 - 9)) = -13// 以不同于集合首元素的初始元素計(jì)算通常也很有用List(1, 7, 2, 9).foldLeft(0)(_ - _)// 相當(dāng)于(((0 - 1) - 7) - 2) - 9// 也可以像下面這樣代替foldLeft操作(0 /: List(1, 7, 2, 9))(_ - _)// 同樣也提供了foldRight或:/// scanLeft和scanRight方法將折疊和映射操作結(jié)合在一起scala> (1 to 10).scanLeft(0)(_ + _)res0: scala.collection.immutable.IndexedSeq[Int] = Vector(0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55)scala> (1 to 10).scanRight(0)(_ + _)res1: scala.collection.immutable.IndexedSeq[Int] = Vector(55, 54, 52, 49, 45, 40, 34, 27, 19, 10, 0)

13.9 拉鏈操作

拉鏈操作指的是兩個(gè)集合,把它們相互對(duì)應(yīng)的元素結(jié)合在一起。

val prices = List(5.0, 20.0, 9.95)val quantities = List(10, 2, 1)// zip方法將它們組合成一個(gè)對(duì)偶的列表prices zip quantities// 得到List[(Double, Int)] = List((5.0, 10), (20.0, 2), (9.95, 1))// 獲得價(jià)格的列表(prices zip quantities) map { p => p._1 * p._2 }// 所有物件的總價(jià)((prices zip quantities) map { p => p._1 * p._2 }).sum// 如果元素長(zhǎng)度不等,則返回最終元素的個(gè)數(shù)與較短的一致// zipAll方法可以指定較短列表的缺省值List(5.0, 20.1, 9.4).zipAll(List(10, 2), 1.2, 1)// 其中zipAll第二個(gè)參數(shù)用于填充前面的列表;而第三個(gè)參數(shù)填充后面的列表// zipWithIndex返回對(duì)偶列表,其中每個(gè)對(duì)偶中第二個(gè)組成部分是每個(gè)元素的下標(biāo)"Scala".zipWithIndexres4: scala.collection.immutable.IndexedSeq[(Char, Int)] = Vector((S,0), (c,1), (a,2), (l,3), (a,4))

13.10 迭代器

可以用iterator方法從集合獲得一個(gè)迭代器。

對(duì)于完整構(gòu)造需要很大開銷的集合而言,迭代器很有用。

Source.fromFile產(chǎn)出一個(gè)迭代器。另外Iterable中的一些方法(grouped或sliding)產(chǎn)生迭代器。

13.11 流

流(stream)提供的是一個(gè)不可變的替代品,是一個(gè)尾部被懶計(jì)算的不可變列表。

def numsFrom(n: BigInt): Stream[BigInt] = n #:: numsFrom(n + 1)// 通過(guò)#::構(gòu)建流val tenOrMore = numsFrom(10)// 返回 Stream(10, ?)tenOrMore.tail.tail.tail// 返回 Stream(13, ?)// 強(qiáng)制求所有值tenOrMore.take(5).force// 可以從迭代器構(gòu)造一個(gè)流val Words = Source.fromFile("/usr/share/dict/words").getLines.toStreamwords // Stream(A, ?)words(5) // Aachenwords // Stream(A, A's, AOL, AOL's, Aachen, ?)// 迭代器對(duì)于每行只能訪問(wèn)一次,而流將緩存訪問(wèn)過(guò)的行,允許重新訪問(wèn)

13.12 懶視圖

使用view方法,同樣可以獲得一個(gè)被懶執(zhí)行的集合。

import scala.math._val powers = (0 until 1000).view.map(pow(10, _))// powers: scala.collection.SeqView[Double,Seq[_]] = SeqViewM(...)powers(2)// res12: Double = 100.0// 也可以用force方法進(jìn)行強(qiáng)制求值。

13.13 與Java集合的互操作

從Scala集合到Java集合的轉(zhuǎn)換

隱式函數(shù) scala.collection類型 java.util類型
asJavaCollection Iterable Collection
asJavaIterable Iterable Iterable
asJavaIterator Iterator Iterator
asJavaEnumeration Iterator Enumeration
seqAsJavaList Seq List
mutableSeqAsjavaList mutable.Seq List
bufferAsJavaList mutable.Buffer List
setAsJavaSet Set Set
mutableSetAsJavaSet mutable.Set Set
mapAsJavaMap Map Map
mutableMapAsJavaMap mutable.Map Map
asJavaDictionary Map Dictionary
asJavaConcurrentMap mutable.ConcurrentMap concurrent.ConcurrentMap

從Java集合到Scala集合的轉(zhuǎn)換

隱式函數(shù) java.util類型 scala.collection類型
collectionAsScalaIterable Collection Iterable
IterableAsScalaIterable Iterable Iterable
asScalaIterator Iterator Iterator
enumerationAsScalaIterator Enumeration Iterator
asScalaBuffer List mutable.Buffer
asScalaSet Set mutable.Set
mapAsScalaMap Map mutable.Map
dictionaryAsScalaMap Dictionary mutable.Map
propertiesAsScalaMap Properties mutable.Map
asScalaConcurentMap concurrent.ConcurrentMap mutable.ConcurrentMap

13.14 線程安全的集合

Scala類庫(kù)提供了六個(gè)特質(zhì),可以將它們混入集合,讓集合的操作變成同步的:

SynchronizedBufferSynchronizedMapSynchronizedPriorityQueueSynchronizedQueueSynchronizedSetSynchronizedStack

示例

val scores = new scala.collection.mutable.HashMap[String, Int] with scala.collection.mutable.SynchronizedMap[String, Int]

13.15 并行集合

par方法產(chǎn)出集合的一個(gè)并行實(shí)現(xiàn)。

// 通過(guò)par并行化for循環(huán),結(jié)果是按線程產(chǎn)出的順序輸出的scala> for (i <- (0 until 100).par) print(i + " ")50 25 0 75 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 26 27 28 29 30 31 32 33 34 35 36 37 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 38 39 40 41 42 43 44 45 46 47 48 49 87 88 89 90 91 92 93 94 95 96 97 98 99 81 82 83 84 85 86 78 79 80 76 77// 在for/yield循環(huán)中,結(jié)果是依次組裝的scala> for (i <- (0 until 100).par) yield i + " "res15: scala.collection.parallel.immutable.ParSeq[String] = ParVector(0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 16 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 25 , 26 , 27 , 28 , 29 , 30 , 31 , 32 , 33 , 34 , 35 , 36 , 37 , 38 , 39 , 40 , 41 , 42 , 43 , 44 , 45 , 46 , 47 , 48 , 49 , 50 , 51 , 52 , 53 , 54 , 55 , 56 , 57 , 58 , 59 , 60 , 61 , 62 , 63 , 64 , 65 , 66 , 67 , 68 , 69 , 70 , 71 , 72 , 73 , 74 , 75 , 76 , 77 , 78 , 79 , 80 , 81 , 82 , 83 , 84 , 85 , 86 , 87 , 88 , 89 , 90 , 91 , 92 , 93 , 94 , 95 , 96 , 97 , 98 , 99 )

par方法返回的并行集合的類型為擴(kuò)展自ParSeq、ParSet或ParMap特質(zhì)的類型,所有這些特質(zhì)都是ParIterable的子類型。但這些并不是Iterable的子類型,因此不能講并行集合傳遞給預(yù)期Iterable、Seq、Set或Map的方法。

13.16 習(xí)題解答

1. 編寫一個(gè)函數(shù),給定字符串,產(chǎn)出一個(gè)包含所有字符的下標(biāo)的映射。舉例來(lái)說(shuō):indexes(“Mississippi”)應(yīng)返回一個(gè)映射,讓’M’對(duì)應(yīng)集{0},’i’對(duì)應(yīng)集{1,4,7,10},依此類推。使用字符到可變集的映射。另外,你如何保證集是經(jīng)過(guò)排序的?

import scala.collection.mutableobject One { def indexes(str: String): mutable.HashMap[Char, mutable.SortedSet[Int]] = { val map = new mutable.HashMap[Char, mutable.SortedSet[Int]] var i = 0 str.foreach { c => map.get(c) match { case Some(value) => map(c) = value + i case None => map += (c -> mutable.SortedSet { i }) } i += 1 } map } def main(args: Array[String]) { println(indexes("Mississippi")) }}// 結(jié)果Map(M -> TreeSet(0), s -> TreeSet(2, 3, 5, 6), p -> TreeSet(8, 9), i -> TreeSet(1, 4, 7, 10))

2. 重復(fù)前一個(gè)練習(xí),這次用字符到列表的不可變映射。

import scala.collection.{immutable, mutable}object Two { def indexes(str: String): mutable.HashMap[Char, immutable.ListSet[Int]] = { val map = new mutable.HashMap[Char, immutable.ListSet[Int]] var i = 0 str.foreach { c => map.get(c) match { case Some(value) => map(c) = value + i case None => map += (c -> immutable.ListSet { i }) } i += 1 } map } def main(args: Array[String]) { println(indexes("Mississippi")) }}// 結(jié)果Map(M -> ListSet(0), s -> ListSet(6, 5, 3, 2), p -> ListSet(9, 8), i -> ListSet(10, 7, 4, 1))

3. 編寫一個(gè)函數(shù),從一個(gè)整型鏈表中去除所有的零值。

object Three { def removeZero(list: List[Int]): List[Int] = { list.filter(_ != 0) } def main(args: Array[String]) { println(removeZero(List(3, 25, 0, 2, 0, 0))) }}

4. 編寫一個(gè)函數(shù),接受一個(gè)字符串的集合,以及一個(gè)從字符串到整數(shù)值的映射。返回整型的集合,其值為能和集合中某個(gè)字符串相對(duì)應(yīng)的映射的值。舉例來(lái)說(shuō),給定Array(“Tom”,”Fred”,”Harry”)和Map(“Tom”->3,”Dick”->4,”Harry”->5),返回Array(3,5)。提示:用flatMap將get返回的Option值組合在一起

object Four { def filterMap(arr: Array[String], map: Map[String, Int]) = { // map.get返回Some(v), 才會(huì)被返回 arr.flatMap(map.get) } def main(args: Array[String]) { println(filterMap(Array("Tom", "Fred", "Harry"), Map("Tom" -> 3, "Dick" -> 4, "Harry" -> 5)).mkString(",")) }}

5. 實(shí)現(xiàn)一個(gè)函數(shù),作用與mkString相同,使用reduceLeft。

import scala.collection.mutabletrait MktString { this: mutable.Iterable[String] => def mktString(split: String = "") = if (this != Nil) this.reduceLeft(_ + split + _)}object Five { def main(args: Array[String]) { var test = new scala.collection.mutable.HashSet[String] with MktString test += "1" test += "2" test += "3" println(test.mktString(",")) }}// 結(jié)果3,1,2

6. 給定整型列表lst,(lst :/ List[Int]())(_ :: _)得到什么?(List[Int]() /: lst)(_ :+ _)又得到什么?如何修改它們中的一個(gè),以對(duì)原列表進(jìn)行反向排序?

object Six { def main(args: Array[String]) { val lst = List(1, 2, 3, 4, 5) // foldRight方法, List[Int]()是初始值(為空)被增加到右邊 // :: 與 +:相同, 頭部添加 println((lst :/ List[Int]()) (_ :: _)) // foldLeft方法, List[Int]()是初始值(為空)被增加到左邊 // :+ 尾部添加 println((List[Int]() /: lst) (_ :+ _)) println((List[Int]() /: lst) ((a, b) => b :: a)) }}// 結(jié)果List(1, 2, 3, 4, 5)List(1, 2, 3, 4, 5)List(5, 4, 3, 2, 1)

7. 在13.11節(jié)中,表達(dá)式(prices zip quantities) map { p => p.1 * p.2}有些不夠優(yōu)雅。我們不能用(prices zip quantities) map { * _ },因?yàn)?_ * _ 是一個(gè)帶兩個(gè)參數(shù)的函數(shù),而我們需要的是一個(gè)帶單個(gè)類型為元組的參數(shù)的函數(shù),F(xiàn)unction對(duì)象的tupled方法可以將帶兩個(gè)參數(shù)的函數(shù)改為以元組為參數(shù)的函數(shù)。將tupled應(yīng)用于乘法函數(shù),以使我們可以用它來(lái)映射由對(duì)偶組成的列表。

object Seven { def main(args: Array[String]) { val prices = List(5.0, 20.0, 9.95) val quantities = List(10, 2, 1) println((prices zip quantities) map { Function.tupled(_ * _) }) }}

8. 編寫一個(gè)函數(shù)。將Double數(shù)組轉(zhuǎn)換成二維數(shù)組。傳入列數(shù)作為參數(shù)。舉例來(lái)說(shuō),Array(1,2,3,4,5,6)和三列,返回Array(Array(1,2,3),Array(4,5,6))。用grouped方法。

object Eight { def toMultiDim(arr: Array[Double], n: Int): Array[Array[Double]] = { // grouped產(chǎn)出下標(biāo)為[0, n)的元素,然后是[n, 2*n)的元素 arr.grouped(n).toArray } def main(args: Array[String]) { val arr = Array(1.0, 2, 3, 4, 5, 6) toMultiDim(arr, 3).foreach(a => println(a.mkString(","))) }}

9. Harry Hacker寫了一個(gè)從命令行接受一系列文件名的程序。對(duì)每個(gè)文件名,他都啟動(dòng)一個(gè)新的線程來(lái)讀取文件內(nèi)容并更新一個(gè)字母出現(xiàn)頻率映射,聲明為:

val frequencies = new scala.collection.multable.HashMap[Char,Int] with scala.collection.mutable.SynchronizedMap[Char,Int]

當(dāng)讀到字母c時(shí),他調(diào)用

frequencies(c) = frequencies.getOrElse(c,0) + 1

為什么這樣做得不到正確答案?如果他用如下方式實(shí)現(xiàn)呢:

import scala.collection.JavaConversions.asScalaConcurrentMapval frequencies:scala.collection.mutable.ConcurrentMap[Char,Int] = new java.util.concurrent.ConcurrentHashMap[Char,Int]

并發(fā)修改集合不安全。

10. Harry Hacker把文件讀取到字符串中,然后想對(duì)字符串的不同部分用并行集合來(lái)并發(fā)地更新字母出現(xiàn)頻率映射。他用了如下代碼:

val frequencies = new scala.collection.mutable.HashMap[Char,Int]for(c <- str.par) frequencies(c) = frequencies.getOrElse(c,0) + 1

為什么說(shuō)這個(gè)想法很糟糕?要真正地并行化這個(gè)計(jì)算,他應(yīng)該怎么做呢?(提示:用aggregate)

并行修改共享變量,結(jié)果無(wú)法估計(jì)。

object Ten { def main(args: Array[String]) { val str = "abdcsdcd" // aggregate將操作符應(yīng)用于集合的不同部分 val frequencies = str.par.aggregate(HashMap[Char, Int]())( { (a, b) => a + (b -> (a.getOrElse(b, 0) + 1)) } , { (map1, map2) => (map1.keySet ++ map2.keySet).foldLeft(HashMap[Char, Int]()) { (result, k) => result + (k -> (map1.getOrElse(k, 0) + map2.getOrElse(k, 0))) } } ).foreach(println) }}// 結(jié)果(s,1)(a,1)(b,1)(c,2)(d,3)

第14章 模式匹配和樣例類

14.1 模式匹配

基本用法

str(i) match { case '+' => sign = 1 case '-' => sign = -1 // 可以增加守衛(wèi),同樣也可以用變量 case ch if Character.isDigit(ch) => digit = Character.digit(ch, 10) case _ => sign = 0 // 與switch的default等效}// 如果沒(méi)有模式能匹配,代碼會(huì)拋出MatchError// match也是表達(dá)式,而不是語(yǔ)句sign = ch match { case '+' => 1 case '-' => -1 case _ => 0}

變量模式可能會(huì)與常量表達(dá)式?jīng)_突,例如:

import scala.math._x match { case Pi => ... // Pi是常量,變量必須以小寫字母開頭 case `test` => ... // 如果有一個(gè)小寫字母開頭的常量,需要將它包仔反引號(hào)中 ...}

類型模式

obj match { case x: Int => x case s: String => Integer.parseInt(s) case m: Map[String, Int] => ... // 由于泛型在Java虛擬機(jī)中會(huì)被擦掉,所以別這樣做 case m: Map[_, _] => ... // OK case _: BigInt => Int.maxValue case _ => 0}

匹配數(shù)組、列表或元組

數(shù)組

arr match { case Array(0) => "0" // 匹配包含0的數(shù)組 case Array(x, y) => x + " " + y // 匹配任何帶有兩個(gè)元素的數(shù)組,并綁定變量 case Array(0, _*) => "0 ..." // 匹配任何以零開始的數(shù)組 case _ => "something else"}

列表

lst match { case 0 :: Nil => "0" case x :: y :: Nil => x + " " + y case 0 :: tail => "0 ..." case _ => "something else"}

元組

pair match { case (0, _) => "0 ..." case (y, 0) => y + " 0" case _ => "neither is 0"}

提取器

模式是如何匹配數(shù)組、列表和元組的,背后是提取器(extractor)機(jī)制,即帶有從對(duì)象中提取值的unapply或unapplySeq方法的對(duì)象。

正則表達(dá)式是另一個(gè)適合使用提取器的場(chǎng)景。

val pattern = "([0-9]+) ([a-z]+)".r"99 bottles" match { case pattern(num, item) => ...}

變量聲明中的模式

scala> val (x, y) = (1, 2)x: Int = 1y: Int = 2scala> val (q, r) = BigInt(10) /% 3q: scala.math.BigInt = 3r: scala.math.BigInt = 1scala> val Array(first, second, _*) = Array(1, 2, 3, 4, 5)first: Int = 1second: Int = 2

for表達(dá)式中的模式

import scala.collection.JavaConversions.propertiesAsScalaMapfor ((k, v) <- System.getProperties()) println(k + " -> " + v)// 打印值為空白的鍵for ((k, "") <- System.getProperties()) println(k)// 同樣可以使用守衛(wèi)for ((k, v) <- System.getProperties() if v == "") println(k)

14.2 樣例類

樣例類是一種特殊的類,經(jīng)過(guò)優(yōu)化已被用于模式匹配。

基本用法

// 擴(kuò)展自常規(guī)類的樣例類abstract class Amountcase class Dollar(value: Double) extends Amountcase class Currency(value: Double, unit: String) extends Amount// 針對(duì)單例的樣例對(duì)象case object Nothing extends Amount// 示例amt match { case Dollar(v) => "$" + v case Currency(_, u) => "Oh noes, I got " + u case Nothing => ""}

當(dāng)聲明樣例類時(shí),如下幾件事自動(dòng)發(fā)生:

構(gòu)造器中的每一個(gè)參數(shù)都成為val--除非它唄顯示地聲明為var。在伴生對(duì)象中提供apply方法以便不用new關(guān)鍵字創(chuàng)建對(duì)象。提供unapply方法讓模式匹配可以工作將生成toString、equals、hashCode和copy方法

copy方法和帶名參數(shù)

val amt = Currency(29.94, "EUR")// copy方法創(chuàng)建一個(gè)與現(xiàn)有對(duì)象相同的新對(duì)象val price = amt.copy()// 也可以用帶名參數(shù)來(lái)修改某些屬性val price = amt.copy(value = 19.21)

case語(yǔ)句中的中值表示法

如果unapply方法產(chǎn)出一個(gè)對(duì)偶,則可以在case語(yǔ)句中使用中置表示法。

case class ::[E](head: E, tail: List[E]) extends List[E]lst match { case h :: t => ... }// 等同于case ::(h, t),將調(diào)用::.unapply(lst)

匹配嵌套結(jié)構(gòu)

例如,某個(gè)商店售賣的物品。有時(shí),我們會(huì)將物品捆綁在一起打折銷售

abstract class Itemcase class Article(description: String, price: Double) extends Itencase class Bundle(description: String, discount: Double, item: Item*) extends Item// 使用Bundle("Father's day special", 20.0, Article("Scala for the Impatient", 39.95), Bundle("Anchor Distillery Sampler", 10.0) Article("Old potrero Straight Rye Whisky", 69.21), Article("Junipero Gin", 79.95),// 模式可以匹配到特定的嵌套case Bundle(_, _, Article(descr, _), _*) => ...// 可以使用@表示法將嵌套的值綁到變量// 以下是一個(gè)計(jì)算某Item價(jià)格的函數(shù)def price(it: Item): Double = it match { case Article(_, p) => p case Bundle(_, disc, its @ _*) => its.map(price _).sum - disc}

密封類

超類聲明為sealed,就是密封類,其子類都必須在與該密封類相同的文件中定義。而且在編譯期所有子類都是可知的,因而編譯器可以檢查模式語(yǔ)句的完整性。

sealed abstract class Amountcase class Dollar(value: Double) extends Amountcase class Currency(value: Double, unit: String) extends Amount

模擬枚舉

sealed abstract class TrafficLightColorcase object Red extends TrafficLightColorcase object Yellow extends TrafficLightColorcase object Green extends TrafficLightColorcolor match { case Red => "stop" case Yellow => "hurry up" case Green => "go"}

14.3 Option類型

標(biāo)準(zhǔn)類庫(kù)中的Option類型用樣例類來(lái)表示那種可能存在、也可能不存在的值。

樣例子類Some包裝了某個(gè)值,例如:Some(“Fred”)。而樣例對(duì)象None表示沒(méi)有值。

Option支持泛型。例如Some(“Fred”)的類型為Option[String]。

14.4 偏函數(shù)

被包在花括號(hào)內(nèi)的一組case語(yǔ)句是一個(gè)便函數(shù),即一個(gè)并非對(duì)所有輸入值都有定義的函數(shù)。它是PartialFunction[A, B]類的一個(gè)實(shí)例。A是參數(shù)類型,B是返回類型。該類有兩個(gè)方法:apply方法從匹配到的模式計(jì)算函數(shù)值,而isDefinedAt方法在輸入至少匹配其中一個(gè)模式時(shí)返回true。

val f: PartialFunction[Char, Int] = { case '+' => 1; case '-' => -1 }f('-') // 調(diào)用f.apply('-'),返回-1f.isDefinedAt('0') // falsef('0') // 拋出MatchError

14.5 習(xí)題解答

1. JDK發(fā)行包有一個(gè)src.zip文件包含了JDK的大多數(shù)源代碼。解壓并搜索樣例標(biāo)簽(用正則表達(dá)式case [^:]+:)。然后查找以//開頭并包含[Ff]alls?thr的注釋,捕獲類似// Falls through或// just fall thru這樣的注釋。假定JDK的程序員們遵守Java編碼習(xí)慣,在該寫注釋的地方寫下了這些注釋,有多少百分比的樣例是會(huì)掉入到下一個(gè)分支的?

2. 利用模式匹配,編寫一個(gè)swap函數(shù),接受一個(gè)整數(shù)的對(duì)偶,返回對(duì)偶的兩個(gè)組成部件互換位置的新對(duì)偶

object Two extends App { def swap[S, T](tup: (S, T)) = { tup match { case (a, b) => (b, a) } } println(swap[String, Int](("1", 2)))}

3. 利用模式匹配,編寫一個(gè)swap函數(shù),交換數(shù)組中的前兩個(gè)元素的位置,前提條件是數(shù)組長(zhǎng)度至少為2

object Three extends App { def swap(arr: Array[Any]) = { arr match { case Array(first, second, rest@_*) => Array(second, first) ++ rest case _ => arr } } println(swap(Array("1", "2", "3", "4")).mkString(","))}// 結(jié)果2,1,3,4

4. 添加一個(gè)樣例類Multiple,作為Item的子類。舉例來(lái)說(shuō),Multiple(10,Article(“Blackwell Toster”,29.95))描述的是10個(gè)烤面包機(jī)。當(dāng)然了,你應(yīng)該可以在第二個(gè)參數(shù)的位置接受任何Item,無(wú)論是Bundle還是另一個(gè)Multiple。擴(kuò)展price函數(shù)以應(yīng)對(duì)新的樣例。

object Four extends App { abstract class Item case class Multiple(num: Int, item: Item) extends Item case class Article(description: String, price: Double) extends Item case class Bundle(description: String, discount: Double, item: Item*) extends Item def price(it: Item): Double = it match { case Article(_, a) => a case Bundle(_, disc, its@_*) => its.map(price).sum - disc case Multiple(n, t) => n * price(t) } val p = price(Multiple(10, Article("Blackwell Toster", 29.95))) println(p)}// 結(jié)果299.5

5. 我們可以用列表制作只在葉子節(jié)點(diǎn)存放值的樹。舉例來(lái)說(shuō),列表((3 8) 2 (5))描述的是如下這樣一棵樹:

. / | / . 2 . / / |3 8 5

不過(guò),有些列表元素是數(shù)字,而另一些是列表。在Scala中,你不能擁有異構(gòu)的列表,因此你必須使用List[Any]。編寫一個(gè)leafSum函數(shù),計(jì)算所有葉子節(jié)點(diǎn)中的元素之和,用模式匹配來(lái)區(qū)分?jǐn)?shù)字和列表。

object Five extends App { def leafSum(list: List[Any]): Int = { var total = 0 list.foreach { case l: List[Any] => total += leafSum(l) case i: Int => total += i } total } val l: List[Any] = List(List(3, 8), 2, List(5)) println(leafSum(l))}// 結(jié)果18

6. 制作這樣的樹更好的做法是使用樣例類。我們不妨從二叉樹開始。

sealed abstract class BinaryTreecase class Leaf(value : Int) extends BinaryTreecase class Node(left : BinaryTree,right : BinaryTree) extends BinaryTree

編寫一個(gè)函數(shù)計(jì)算所有葉子節(jié)點(diǎn)中的元素之和。

object Six extends App { sealed abstract class BinaryTree case class Leaf(value: Int) extends BinaryTree case class Node(left: BinaryTree, right: BinaryTree) extends BinaryTree def leafSum(tree: BinaryTree): Int = { tree match { case Node(a, b) => leafSum(a) + leafSum(b) case Leaf(v) => v } } val r = Node(Leaf(3), Node(Leaf(3), Leaf(9))) println(leafSum(r))}// 結(jié)果15

7. 擴(kuò)展前一個(gè)練習(xí)中的樹,使得每個(gè)節(jié)點(diǎn)可以有任意多的后代,并重新實(shí)現(xiàn)leafSum函數(shù)。第五題中的樹應(yīng)該能夠通過(guò)下述代碼表示:

Node(Node(Leaf(3),Leaf(8)),Leaf(2),Node(Leaf(5)))

object Seven extends App { sealed abstract class BinaryTree case class Leaf(value: Int) extends BinaryTree case class Node(tr: BinaryTree*) extends BinaryTree def leafSum(tree: BinaryTree): Int = { tree match { case Node(r @ _*) => r.map(leafSum).sum case Leaf(v) => v } } val r = Node(Node(Leaf(3), Leaf(8)), Leaf(2), Node(Leaf(5))) println(leafSum(r))}// 結(jié)果18

8. 擴(kuò)展前一個(gè)練習(xí)中的樹,使得每個(gè)非葉子節(jié)點(diǎn)除了后代之外,能夠存放一個(gè)操作符。然后編寫一個(gè)eval函數(shù)來(lái)計(jì)算它的值。舉例來(lái)說(shuō):

+ / | / * 2 - / / |3 8 5

上面這棵樹的值為(3 * 8) + 2 + (-5) = 21

object Eight extends App { sealed abstract class BinaryTree case class Leaf(value: Int) extends BinaryTree case class Node(op: Char, leafs: BinaryTree*) extends BinaryTree def eval(tree: BinaryTree): Int = { tree match { case Node(op, leafs@_*) => op match { case '+' => leafs.map(eval).sum case '-' => -leafs.map(eval).sum case '*' => leafs.map(eval).product } case Leaf(v) => v } } val x = Node('+', Node('*', Leaf(3), Leaf(8)), Leaf(2), Node('-', Leaf(5))) println(x) println(eval(x))}// 結(jié)果Node(+,WrappedArray(Node(*,WrappedArray(Leaf(3), Leaf(8))), Leaf(2), Node(-,WrappedArray(Leaf(5)))))21

9. 編寫一個(gè)函數(shù),計(jì)算List[Option[Int]]中所有非None值之和。不得使用match語(yǔ)句。

object Nine extends App { def sum(lst: List[Option[Int]]) = lst.map(_.getOrElse(0)).sum val x = List(Some(1), None, Some(2), None, Some(3)) println(sum(x))}

10. 編寫一個(gè)函數(shù),將兩個(gè)類型為Double=>Option[Double]的函數(shù)組合在一起,產(chǎn)生另一個(gè)同樣類型的函數(shù)。如果其中一個(gè)函數(shù)返回None,則組合函數(shù)也應(yīng)返回None。例如:

def f(x : Double) = if ( x >= 0) Some(sqrt(x)) else Nonedef g(x : Double) = if ( x != 1) Some( 1 / ( x - 1)) else Noneval h = compose(f,g)

h(2)將得到Some(1),而h(1)和h(0)將得到None

object Ten extends App { def compose(f: Double => Option[Double], g: Double => Option[Double]) = { (x: Double) => if (f(x).isEmpty || g(x).isEmpty) None else g(x) } import scala.math.sqrt def f(x: Double) = if (x >= 0) Some(sqrt(x)) else None def g(x: Double) = if (x != 1) Some(1 / (x - 1)) else None val h = compose(f, g) println(h(0)) println(h(1)) println(h(2))}// 結(jié)果Some(-1.0)NoneSome(1.0)

參考

習(xí)題解答


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 呼和浩特市| 广丰县| 武清区| 明溪县| 章丘市| 常州市| 德州市| 铜梁县| 南平市| 蒲城县| 苗栗县| 仙居县| 保靖县| 花莲市| 黑河市| 三门县| 保德县| 龙井市| 道孚县| 西林县| 琼海市| 霍林郭勒市| 望奎县| 留坝县| 建昌县| 南和县| 施甸县| 虎林市| 石首市| 黔东| 博白县| 南部县| 新宾| 无棣县| 布拖县| 喜德县| 上杭县| 乌兰察布市| 寻甸| 洪泽县| 儋州市|