官术网_书友最值得收藏!

2.6 字符串的逐個字符處理

問題

你想知道如何遍歷一個字符串中的每個字符,并在遍歷字符串時對每個字符執(zhí)行操作。

解決方案

如果需要轉換字符串中的字符以得到一個新的結果(而不是副作用),應該使用for表達式或者高階函數(shù)(Higher-Order Function,HOF)(如map和filter)。如果想做一些有副作用的的操作,如打印輸出,那么應該使用簡單的for循環(huán)或者使用像foreach這樣的方法。如果需要把字符串當作一個字節(jié)序列,可以使用getBytes方法。

轉換

以下是一個for表達式的例子,它是一個帶有yield的for循環(huán),對字符串中每一個字符進行轉換:

下面是與之等價的map方法:

這段代碼還可以使用Scala的下劃線字符來變得更短:

使用高階函數(shù)和純轉換函數(shù)的好處是,可以將它們串聯(lián)起來,得到想要的結果。下面是一個先調用filter然后調用map的例子:

副作用

當需要執(zhí)行一個副作用時,如將一個字符串中的每個字符輸出到STDOUT,就可以使用一個簡單for循環(huán):

也可以使用foreach方法:

處理字符串字節(jié)

如果需要將一個字符串作為一個字節(jié)序列來處理,也可以使用getBytes方法。getBytes返回一個字符串的字節(jié)序列集合:

在getBytes之后添加foreach展示了對每個字節(jié)進行操作的一種方式:

寫一個能傳入map的方法

要寫一個可以傳入map的方法來對字符串中的字符進行操作,需要定義一個Char作為輸入,然后在方法中對該Char進行邏輯運算。當運算完成后,返回你的算法所需的數(shù)據(jù)類型。雖然下面的算法很短,但它演示了如何創(chuàng)建一個自定義方法并將該方法傳入map:

請參閱10.2節(jié)中對Eta Expansion的討論,以了解更多關于如何將一個方法傳遞給另一個以函數(shù)作為參數(shù)的方法的細節(jié)。

討論

因為Scala將字符串視為一個字符序列Seq[Char],所以下面的所有例子都能正常工作。

for+yield

如果你是從命令式語言(Java、C、C#等)轉到Scala,一開始使用map方法可能會不太適應。在這種情況下,你可能更愿意寫一個類似于這樣的for表達式:

在for循環(huán)中加入yield,本質上是將每次循環(huán)迭代的結果放入一個臨時的保留區(qū)域。當循環(huán)完成時,保留區(qū)域中的所有元素將作為一個單一的集合返回,也可以說它們是由for循環(huán)產(chǎn)生的。

雖然我(強烈)建議你熟悉map方法的工作原理,但如果想在剛開始就使用for表達式,那么需要了解下面使用filter和map方法的表達式:

與下面的for表達式等價:

幾乎不用寫自定義的for循環(huán)

正如我在Scala官方網(wǎng)站上的第一版Scala Book(https://oreil.ly/2mkjT)中所寫的,Scala集合類的一大優(yōu)勢是它們帶有幾十個預置的方法。這樣做的一大好處是,每次需要處理一個集合時,你不再需要編寫自定義的for循環(huán)。如果這聽起來還不夠吸引人的話,它還意味著你不再需要讀別人寫的各種for循環(huán)。

更重要的是,研究表明,開發(fā)人員花在閱讀代碼上的時間遠遠多于編寫代碼的時間,閱讀時間/編寫時間的比例估計最多20:1,最少也有10:1,因為我們花了這么多時間閱讀代碼,所以代碼既要保持簡潔又要具備可讀性是很重要的,這也就是Scala開發(fā)者所說的表現(xiàn)力。

轉換方法

一旦適應了“Scala風格”即利用Scala內置的轉換函數(shù),這樣就不用寫自定義的for循環(huán)了,而是去使用map方法。這兩個map表達式產(chǎn)生的結果都與for表達式相同:

像map這樣的轉換方法既可以接受一個如上所示的單行匿名函數(shù),也可以接受一個更大的代碼塊算法。下面是一個使用多行代碼塊的map的例子:

注意,這個算法是用大括號括起來的,當創(chuàng)建一個像這樣的多行代碼塊時,都需要使用大括號。

你可能會從這些例子中推測出map有一個內置的循環(huán),在這個循環(huán)中,它一次將一個Char傳遞給它的參數(shù)函數(shù)。

在繼續(xù)之前,這里還有幾個字符串轉換方法的例子:

副作用方法

map或for/yield方法用于將一個集合轉換為另一個集合,而foreach方法用來對每個元素進行操作而不返回結果,這完全可以從它的方法簽名的返回值是Unit推斷出來:

所以,foreach能很好地處理副作用,比如輸出:

一個完整的例子

下面的例子演示了如何在一個字符串上調用getBytes,然后將一個代碼塊傳入foreach,用來計算一個字符串的Adler-32校驗值(https://oreil.ly/xbTRd):

@main方法中的第二個println語句輸出十六進制值11e60398,這與Adler-32算法頁面上的0x11E60398相匹配。

請注意,我在這個例子中使用了foreach而不是map,目的是在字符串的每個字節(jié)上循環(huán),然后對每個字節(jié)做一些操作,而不從循環(huán)中返回任何東西。與map不同的是,這個算法會更新可變變量a和b。

另見

·Scala編譯器會將for循環(huán)翻譯成foreach方法調用。如果循環(huán)中有一個或多個if語句(守衛(wèi))或yield表達式,情況則會變得復雜的多。這在我的Functional Programming,Simplifiedhttps://oreil.ly/wKfgQ)(CreateSpace出版社)一書中有詳細討論。

·Adler代碼基于維基百科中對Adler-32校驗值算法的討論(https://oreil.ly/xbTRd)。

主站蜘蛛池模板: 涿州市| 朝阳区| 明水县| 清河县| 鹰潭市| 梨树县| 忻城县| 平凉市| 赤峰市| 海丰县| 石台县| 宣城市| 竹北市| 巩留县| 兴宁市| 舒兰市| 佛学| 青川县| 瓦房店市| 竹山县| 建昌县| 台东县| 济源市| 和顺县| 元谋县| 河南省| 锦州市| 吴川市| 合肥市| 铜梁县| 天峨县| 乐至县| 微博| 开封县| 星子县| 顺昌县| 岳阳县| 桂阳县| 宜黄县| 临安市| 鹤山市|