入门 Spark 的路上很难不接触 Scala 。 Scala 似乎是为 java 提供了很多『类似函数式编程』的语法糖,这里记录一下这个语言独特的地方分享给读者朋友们。

参考资料主要有:

  • 曹洁 . Spark大数据分析技术(Scala版)[M]. 北京航空航天大学出版社, 2021. ISBN:9787512433854
  • 陈欢 , 林世飞 . Spark最佳实践[M]. 人民邮电出版社, 2016. ISBN:9787115422286

Scala 基本思想与注意事项

Sacla 即 Scalable Language ,正如其名,是一门可伸缩的编程语言:

  • 基于 java 的虚拟机( Scala 会被编译成 JVM 字节码)
  • 但是既可以当脚本使用,又可以构造大型系统
  • 是静态语言,但是可以像动态语言那样支持交互式编程
  • 面型对象:每一个值都是对象,每一次运算都是一次方法调用
  • 函数式编程:所有函数都是对象,函数是“一等公民”
  • Scala 中几乎一切都是表达式

scala 是解释器, scalac 是编译器;可以直接 scala test.scala ,也可以 scalac test.scala & scala test (先把源码编译为字节码,再把字节码放到虚拟机中解释运行)。还可用输入 scala 进入交换编程界面。

所以要注意的是,需要先安装 JDK ,并且设置好环境变量 JAVA_HOME 。此外,更加重要的是, Scala 小版本兼容:2.12.x2.13.x 这两者不兼容,2.12.102.12.11 才兼容。

最基本的语法示例

类型的声明、控制结构(for、模式匹配、case)

// 变量
val two: Int = 1 + 1 var one: Int = 1
var one: String = 'one' // 函数
def addOne(x: Int): Int = x + 1 def add(x: Int, y: Int): Int = {
x + y
} // 部分控制结构
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]

关于 for 还有一些奇技淫巧。

// 多个区间
for (a <- 1 to 2; b <- 1 to 2) {
println("a: " + a + ", b: " + b)
}
// 结果
a: 1, b: 1
a: 1, b: 2
a: 2, b: 1
a: 2, b: 2 // 过滤器
val list1 = List(3, 5, 2, 1, 7)
for (x <- list1 if x % 2 == 1) print(" " + x)
// 3 5 1 7

关于模式匹配,则有更多奇技淫巧。这里我直接参考:scala中case的用法

// 一.简单匹配,值匹配:

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)
}
} // 三 根据顺序匹配 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 的数组匹配 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)
}
} // 五 对象深度匹配 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 + "?")
}
} // 六 正则表达式匹配 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)
}
}

关于 case ,我想强调其在“解包”中的应用:

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 的大括号。与下面等效。

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 语法独特的地方

  1. 无参数方法,调用时不用加括号:args.isEmpty
def width: Int = if (height == 0) 0 else contents(0).length

width  // 调用
  1. for 中使用 <- ,相当于 Python 的 in

  2. 继承用关键字 extendsclass A(a: Int) extends B

  3. 单实例对象 / 静态成员变量与方法定义在 object 中:

object Timer {
var count = 0
def currentCount() : Long = {
count += 1
count
}
} Timer.currentCount() // 直接调用 class Timer {
...
}
  1. 函数返回不必非要加 return ,默认最后一个表达式。

  2. 函数式:匿名函数作为参数,并且还可以更简洁

val numbers = List(1, -3, -5, 9, 0)

numbers.filter((x) => x > 0)
numbers.filter(x => x > 0)
numbers.filter(_ > 0) // 一个参数且函数中仅被使用一次时
  1. _ 具有特殊的意义与工作(占位)
// 部分应用函数
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 的面向对象与一等公民“函数”

(1).+(2)  // 3

如上,(1)是对象,.+(2)是方法调用。 Scala 中万物皆对象。

var increase = (x: Int) => x + 1

如上,函数是一等公民,可以赋值给变量。

基本数据结构

有以下概念:

  • 不可变列表 List 与可变列表 ListBuffer
  • 定长数组 Array 与变长数组 ArrayBuffer
  • 不可变集合 Set 与可变集合 scala.collection.mutable.Set
  • 映射 Map 与 可变映射 scala.collection.mutable.Map
  • 元组 Tuple

注意事项与 Scala 奇技淫巧

  1. 使用 until 是遍历数组的好办法,by_* 特殊意义:
for (i <- 0 until.length) { }

Array (1,3,5,7,9,11)  // 等价于
Array[Int](1 to 11 by 2:_*) // _* 有种解包的意味
  1. 使用 yield 生成数组
val a = Array(1, 2, 3, 4)
val res1 = for (ele <- a) yield 2 * ele
// 2, 4, 6, 8
  1. 元组的下标从 1 开始
val person = (1, 2, "ABC")
person._1 // 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)
<<---------->>
  1. Map 神奇操作
// 创建
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"

对于可变 Map

// += -=
dict += (("Tom", 91), ("Jerry", 87))
dict -= "Tom"
dict -= ("Jerry", "Bob") // ++= --= 与其他集合相联系
dict ++= List(("Tom", 91), ("Jerry", 87))
dict --= List("Jerry", "Bob")
  1. ::::: 创建列表
1::3::5::Nil  // List[Int] = List(1, 3, 5)

注意 :: 是右结合的:(1::(3::(5::Nil)))

// ::: 用来连接列表
val L4 = L3 ::: List("Hadoop", "Hbase")

关于数据结构的讨论(List or Array?)

  • 多用 List 而非 Array
  • 列表的结构是递归的(即链表,linkedList),而数组是平等的

参考:

Scala,一门「特立独行」的语言!的更多相关文章

  1. 「HNOI2004」「LuoguP2292」L语言(AC自动机

    题目描述 标点符号的出现晚于文字的出现,所以以前的语言都是没有标点的.现在你要处理的就是一段没有标点的文章. 一段文章T是由若干小写字母构成.一个单词W也是由若干小写字母构成.一个字典D是若干个单词的 ...

  2. P4715 「英语」Z 语言

    题解: 平衡树维护hash值 为了支持加入删除操作 x*base^y 其中y为他是第k大 同一般的维护方法,我们不用对每个节点维护他的hash值 而是只用记录他的x值(他的位置) 然后通过updata ...

  3. [Luogu4715]「英语」Z 语言

    luogu description 你有一个长度为\(n\)的串\(A\)和一个长度为\(m\)的串\(B\),字符集大小\(2^{31}\),且同一个串中没有相同的元素. 定义\(B\)串与\(A_ ...

  4. 【LOJ】#3046. 「ZJOI2019」语言

    LOJ#3046. 「ZJOI2019」语言 先orz zsy吧 有一个\(n\log^3n\)的做法是把树链剖分后,形成logn个区间,这些区间两两搭配可以获得一个矩形,求矩形面积并 然后就是对于一 ...

  5. 「ZJOI2019」语言 解题报告

    「ZJOI2019」语言 3个\(\log\)做法比较简单,但是写起来还是有点麻烦的. 大概就是树剖把链划分为\(\log\)段,然后任意两段可以组成一个矩形,就是个矩形面积并,听说卡卡就过去了. 好 ...

  6. Go语言:编写一个 WebsiteRacer 的函数,用来对比请求两个 URL 来「比赛」,并返回先响应的 URL。如果两个 URL 在 10 秒内都未返回结果,返回一个 error。

    问题: 你被要求编写一个叫做 WebsiteRacer 的函数,用来对比请求两个 URL 来「比赛」,并返回先响应的 URL.如果两个 URL 在 10 秒内都未返回结果,那么应该返回一个 error ...

  7. javascript——从「最被误解的语言」到「最流行的语言」

    JavaScript曾是"世界上最被误解的语言".由于它担负太多的特性.包含糟糕的交互和失败的设计,但随着Ajax的到来.JavaScript"从最受误解的编程语言演变为 ...

  8. 一个「学渣」从零开始的Web前端自学之路

    从 13 年专科毕业开始,一路跌跌撞撞走了很多弯路,做过餐厅服务员,进过工厂干过流水线,做过客服,干过电话销售可以说经历相当的“丰富”. 最后的机缘巧合下,走上了前端开发之路,作为一个非计算机专业且低 ...

  9. 「MoreThanJava」当大学选择了计算机之后应该知道的

    「MoreThanJava」 宣扬的是 「学习,不止 CODE」,本系列 Java 基础教程是自己在结合各方面的知识之后,对 Java 基础的一个总回顾,旨在 「帮助新朋友快速高质量的学习」. 当然 ...

  10. 「MoreThanJava」机器指令到汇编再到高级编程语言

    「MoreThanJava」 宣扬的是 「学习,不止 CODE」,本系列 Java 基础教程是自己在结合各方面的知识之后,对 Java 基础的一个总回顾,旨在 「帮助新朋友快速高质量的学习」. 当然 ...

随机推荐

  1. 痞子衡嵌入式:瑞萨RA系列FSP固件库分析之外设驱动

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是瑞萨RA系列FSP固件库里的外设驱动. 上一篇文章 <瑞萨RA8系列高性能MCU开发初体验>,痞子衡带大家快速体验了一下瑞萨 ...

  2. linux 基础(9)背景工作管理

    前景和背景工作管理 在 linux 中,进程以调用顺序构成一棵树,系统的初始程序是 systemd,然后一个程序又调用另一个程序.当你在 bash 里输入其他指令,这些指令就作为当前shell 的子进 ...

  3. API和SDK的区别

    API 和 SDK 有以下区别: 定义与功能: API(应用程序编程接口):是一组定义了软件组件之间交互规范的接口,目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而无需访问源码或 ...

  4. Spring SPI、Solon SPI 有点儿像(Maven 与 Gradle)

    一.什么是 SPI SPI 全名 Service Provider interface,翻译过来就是"服务提供接口".基本效果是,申明一个接口,然后通过配置获取它的实现,进而实现动 ...

  5. Discuz7.2 XML漏洞

    Discuz7.2 导入插件数据可以生成一句话木马,直接getshell. 姿势:版主的"管理中心" => "插件" => "导入&quo ...

  6. 看图认识HTML5

    教程: https://www.w3.org/TR/html52 https://www.w3cschool.cn/html5/ https://www.runoob.com/html/html5-i ...

  7. php接收二进制流

    /** 二进制流生成文件 * $_POST 无法解释二进制流,需要用到 $GLOBALS['HTTP_RAW_POST_DATA'] 或 php://input * $GLOBALS['HTTP_RA ...

  8. VL4AD:让语义分割认识未知类别,无需额外数据和训练的OOD语义分割 | ECCV'24

    来源:晓飞的算法工程笔记 公众号,转载请注明出处 论文: VL4AD: Vision-Language Models Improve Pixel-wise Anomaly Detection 论文地址 ...

  9. canvas 隐写术

    http://www.alloyteam.com/2016/03/image-steganography/#prettyPhoto https://www.cnblogs.com/Miracle-ZL ...

  10. MySQL报错注入之Xpath报错&floor函数报错

    目录 前言 Xpath报错注入 updatexml()函数 extractvalue()函数 floor函数报错 count与group by的虚拟表 总结 PostGIS函数报错 ST_LatFro ...