Scala,一門「特立獨(dú)行」的語(yǔ)言!
本文轉(zhuǎn)載自微信公眾號(hào)「Piper蛋窩」,作者Piper蛋 。轉(zhuǎn)載本文請(qǐng)聯(lián)系Piper蛋窩公眾號(hào)。
入門 Spark 的路上很難不接觸 Scala 。Scala 似乎是為 java 提供了很多『類似函數(shù)式編程』的語(yǔ)法糖,這里記錄一下這個(gè)語(yǔ)言獨(dú)特的地方分享給讀者朋友們。
參考資料主要有:
- 曹潔 . Spark大數(shù)據(jù)分析技術(shù)(Scala版)[M]. 北京航空航天大學(xué)出版社, 2021. ISBN:9787512433854
- 陳歡 , 林世飛 . Spark最佳實(shí)踐[M]. 人民郵電出版社, 2016. ISBN:9787115422286
Scala 基本思想與注意事項(xiàng)
Sacla 即 Scalable Language ,正如其名,是一門可伸縮的編程語(yǔ)言:
- 基于 java 的虛擬機(jī)( Scala 會(huì)被編譯成 JVM 字節(jié)碼)
- 但是既可以當(dāng)腳本使用,又可以構(gòu)造大型系統(tǒng)
- 是靜態(tài)語(yǔ)言,但是可以像動(dòng)態(tài)語(yǔ)言那樣支持交互式編程
- 面型對(duì)象:每一個(gè)值都是對(duì)象,每一次運(yùn)算都是一次方法調(diào)用
- 函數(shù)式編程:所有函數(shù)都是對(duì)象,函數(shù)是“一等公民”
- Scala 中幾乎一切都是表達(dá)式
scala 是解釋器, scalac 是編譯器;可以直接 scala test.scala ,也可以 scalac test.scala & scala test (先把源碼編譯為字節(jié)碼,再把字節(jié)碼放到虛擬機(jī)中解釋運(yùn)行)。還可用輸入 scala 進(jìn)入交換編程界面。
所以要注意的是,需要先安裝 JDK ,并且設(shè)置好環(huán)境變量 JAVA_HOME 。此外,更加重要的是, Scala 小版本兼容:2.12.x 與 2.13.x 這兩者不兼容,2.12.10 與 2.12.11 才兼容。
最基本的語(yǔ)法示例
類型的聲明、控制結(jié)構(gòu)(for、模式匹配、case)
- // 變量
- val two: Int = 1 + 1
- var one: Int = 1
- var one: String = 'one'
- // 函數(shù)
- def addOne(x: Int): Int = x + 1
- def add(x: Int, y: Int): Int = {
- x + y
- }
- // 部分控制結(jié)構(gòu)
- var filename =
- if (!args.isEmpty) args(0)
- else "default.txt"
- for (i <- 1 to 4)
- println("iteration " + i)
1 to 4 是 [1,2,3,4] ,而 i until 4 是 [1,2,3] 。
關(guān)于 for 還有一些奇技淫巧。
- // 多個(gè)區(qū)間
- for (a <- 1 to 2; b <- 1 to 2) {
- println("a: " + a + ", b: " + b)
- }
- // 結(jié)果
- a: 1, b: 1
- a: 1, b: 2
- a: 2, b: 1
- a: 2, b: 2
- // 過(guò)濾器
- val list1 = List(3, 5, 2, 1, 7)
- for (x <- list1 if x % 2 == 1) print(" " + x)
- // 3 5 1 7
關(guān)于模式匹配,則有更多奇技淫巧。這里我直接參考:scala中case的用法[1]
- // 一.簡(jiǎn)單匹配,值匹配:
- val bools = List(true, false)
- for (bool <- bools) {
- bool match {
- case true => println("heads")
- case false => println("tails")
- case _ => println("something other than heads or tails (yikes!)")
- }
- }
- import scala.util.Random
- val randomInt = new Random().nextInt(10)
- randomInt match {
- case 7 => println("lucky seven!")
- case otherNumber => println("boo, got boring ol' " + otherNumber)
- }
- // 二. 類型匹配
- val sundries = List(23, "Hello", 8.5, 'q')
- for (sundry <- sundries) {
- sundry match {
- case i: Int => println("got an Integer: " + i)
- case s: String => println("got a String: " + s)
- case f: Double => println("got a Double: " + f)
- case other => println("got something else: " + other)
- }
- }
- // 三 根據(jù)順序匹配
- val willWork = List(1, 3, 23, 90)
- val willNotWork = List(4, 18, 52)
- val empty = List()
- for (l <- List(willWork, willNotWork, empty)) {
- l match {
- case List(_, 3, _, _) => println("Four elements, with the 2nd being '3'.")
- case List(_*) => println("Any other list with 0 or more elements.")
- }
- }
- // 四 case里面用 guard 的數(shù)組匹配
- val tupA = ("Good", "Morning!")
- val tupB = ("Guten", "Tag!")
- for (tup <- List(tupA, tupB)) {
- tup match {
- case (thingOne, thingTwo) if thingOne == "Good" =>
- println("A two-tuple starting with 'Good'.")
- case (thingOne, thingTwo) =>println("This has two things: " + thingOne + " and " + thingTwo)
- }
- }
- // 五 對(duì)象深度匹配
- case class Person(name: String, age: Int)
- val alice = new Person("Alice", 25)
- val bob = new Person("Bob", 32)
- val charlie = new Person("Charlie", 32)
- for (person <- List(alice, bob, charlie)) {
- person match {
- case Person("Alice", 25) => println("Hi Alice!")
- case Person("Bob", 32) => println("Hi Bob!")
- case Person(name, age) =>
- println("Who are you, " + age + " year-old person named " + name + "?")
- }
- }
- // 六 正則表達(dá)式匹配
- val BookExtractorRE = """Book: title=([^,]+),\s+authors=(.+)""".r
- val MagazineExtractorRE = """Magazine: title=([^,]+),\s+issue=(.+)""".r
- val catalog = List(
- "Book: title=Programming Scala, authors=Dean Wampler, Alex Payne",
- "Magazine: title=The New Yorker, issue=January 2009",
- "Book: title=War and Peace, authors=Leo Tolstoy",
- "Magazine: title=The Atlantic, issue=February 2009",
- "BadData: text=Who put this here??"
- )
- for (item <- catalog) {
- item match {
- case BookExtractorRE(title, authors) =>
- println("Book \"" + title + "\", written by " + authors)
- case MagazineExtractorRE(title, issue) =>
- println("Magazine \"" + title + "\", issue " + issue)
- case entry => println("Unrecognized entry: " + entry)
- }
- }
關(guān)于 case ,我想強(qiáng)調(diào)其在“解包”中的應(yīng)用:
- dict = Map("Piper" -> 95, "Bob" -> 90)
- dict.foreach {
- case (k, v) => printf(
- "grade of %s is %s/n", k, v
- )
- }
- grade of Piper is 95
- grade of Bob is 90
上述:使用了 foreach { case () => {} } ,注意 foreach 的大括號(hào)。與下面等效。
- dict = Map("Piper" -> 95, "Bob" -> 90)
- dict.foreach (
- x => println(
- s"grade of ${x._1} is ${x._2}"
- )
- )
- grade of Piper is 95
- grade of Bob is 90
Scala 語(yǔ)法獨(dú)特的地方
無(wú)參數(shù)方法,調(diào)用時(shí)不用加括號(hào):args.isEmpty。
- def width: Int = if (height == 0) 0 else contents(0).length
- width // 調(diào)用
for 中使用 <- ,相當(dāng)于 Python 的 in 。
繼承用關(guān)鍵字 extends :class A(a: Int) extends B 。
單實(shí)例對(duì)象 / 靜態(tài)成員變量與方法定義在 object 中:
- object Timer {
- var count = 0
- def currentCount() : Long = {
- count += 1
- count
- }
- }
- Timer.currentCount() // 直接調(diào)用
- class Timer {
- ...
- }
函數(shù)返回不必非要加 return ,默認(rèn)最后一個(gè)表達(dá)式。
函數(shù)式:匿名函數(shù)作為參數(shù),并且還可以更簡(jiǎn)潔
- val numbers = List(1, -3, -5, 9, 0)
- numbers.filter((x) => x > 0)
- numbers.filter(x => x > 0)
- numbers.filter(_ > 0) // 一個(gè)參數(shù)且函數(shù)中僅被使用一次時(shí)
_ 具有特殊的意義與工作(占位)
- // 部分應(yīng)用函數(shù)
- def adder(m: Int, n: Int) = m + n
- val add2 = adder(2, _: Int) // add2: (Int) => Int = <function1>
- add2(3) // res1: Int = 5
- // 柯里化 currying
- def curriedSum(x: Int)(y: Int) = x + y
- curriedSum (1)(2)
- val onePlus = curriedSum(1)_ // 注意這里使用了 _
- onePlus(2)
- // 模式匹配
- var times = 1
- times match {
- case 1 => "one"
- case 2 => "two"
- case _ => "other"
- }
Scala 的面向?qū)ο笈c一等公民“函數(shù)”
- (1).+(2) // 3
如上,(1)是對(duì)象,.+(2)是方法調(diào)用。Scala 中萬(wàn)物皆對(duì)象。
- var increase = (x: Int) => x + 1
如上,函數(shù)是一等公民,可以賦值給變量。
基本數(shù)據(jù)結(jié)構(gòu)
有以下概念:
- 不可變列表 List 與可變列表 ListBuffer
- 定長(zhǎng)數(shù)組 Array 與變長(zhǎng)數(shù)組 ArrayBuffer
- 不可變集合 Set 與可變集合 scala.collection.mutable.Set
- 映射 Map 與 可變映射 scala.collection.mutable.Map
- 元組 Tuple
注意事項(xiàng)與 Scala 奇技淫巧
使用 until 是遍歷數(shù)組的好辦法,by 和 _* 特殊意義:
- for (i <- 0 until.length) { }
- Array (1,3,5,7,9,11) // 等價(jià)于
- Array[Int](1 to 11 by 2:_* "Int") // _* 有種解包的意味
使用 yield 生成數(shù)組
- val a = Array(1, 2, 3, 4)
- val res1 = for (ele <- a) yield 2 * ele
- // 2, 4, 6, 8
元組的下標(biāo)從 1 開(kāi)始
- val person = (1, 2, "ABC")
- person._1 // 1
拉鏈操作 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)
- <<---------->>
Map 神奇操作
- // 創(chuàng)建
- val dict = Map("Piper" -> 95, "Bob" -> 90)
- val kv = Map(("Piper", 95), ("Bob", 90))
- // 取值
- dict("Piper")
- // 合并 ++
- dict ++ kv
- dict.++(kv)
- // 添加 + ,刪除 -
- val n = dict + ("Tom" -> 91)
- val l = dict - "Tom"
對(duì)于可變 Map :
- // += -=
- dict += (("Tom", 91), ("Jerry", 87))
- dict -= "Tom"
- dict -= ("Jerry", "Bob")
- // ++= --= 與其他集合相聯(lián)系
- dict ++= List(("Tom", 91), ("Jerry", 87))
- dict --= List("Jerry", "Bob")
:: 與 ::: 創(chuàng)建列表
- 1::3::5::Nil // List[Int] = List(1, 3, 5)
注意 :: 是右結(jié)合的:(1::(3::(5::Nil))) 。
- // ::: 用來(lái)連接列表
- val L4 = L3 ::: List("Hadoop", "Hbase")
關(guān)于數(shù)據(jù)結(jié)構(gòu)的討論(List or Array?)
- 多用 List 而非 Array
- 列表的結(jié)構(gòu)是遞歸的(即鏈表,linkedList),而數(shù)組是平等的
參考:
- scala中List、Array、ListBuffer、ArrayList、Set、元組區(qū)別[2]
- Scala學(xué)習(xí)筆記5 (集合 Collections)[3]
參考資料
[1]scala中case的用法: https://blog.csdn.net/qq_41669665/article/details/86158993
[2]scala中List、Array、ListBuffer、ArrayList、Set、元組區(qū)別: https://blog.csdn.net/mar_ljh/article/details/81910286
[3]Scala學(xué)習(xí)筆記5 (集合 Collections): https://blog.csdn.net/lyrebing/article/details/20362227
【責(zé)任編輯:武曉燕 TEL:(010)68476606】