Scala学习之Scala快速入门

Posted 顧棟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala学习之Scala快速入门相关的知识,希望对你有一定的参考价值。

Scala的“味道”

原文地址: https://docs.scala-lang.org/overviews/scala-book/prelude-taste-of-scala.html

在本书中,我们假设您以前使用过 Java 之类的语言,并准备好查看一系列 Scala 示例以了解该语言的外观。 尽管这不是 100% 必要的,但如果您已经下载并安装了 Scala,它也会有所帮助,这样您就可以在进行过程中测试示例。 您还可以使用 ScalaFiddle.io 在线测试这些示例。

Overview 概述

在我们进入示例之前,这里有一些关于 Scala 的重要信息:

  • 这是一种高级语言
  • 它是静态类型的
  • 它的语法简洁但仍然可读——我们称之为富有表现力
  • 它支持面向对象编程 (OOP) 范式
  • 它支持函数式编程 (FP) 范式
  • 它有一个复杂的类型推断系统
  • Scala 代码生成在 Java 虚拟机 (JVM) 上运行的 .class 文件
  • 在 Scala 中使用 Java 库很容易

Hello, world

从《C 编程语言》这本书开始,以“Hello, world”示例开始编程书籍一直是一种传统,不要让人失望,这是在 Scala 中编写该示例的一种方式:

object Hello extends App {
    println("Hello, world")
}

将该代码保存到名为 Hello.scala 的文件后,您可以使用scalac 编译它:

$ scalac Hello.scala

如果您是从 Java 来到 Scala,scalac 就像 javac,该命令会创建两个文件:

  • Hello$.class
  • Hello.class

这些与您使用 javac 创建的“.class”字节码文件相同,它们已准备好在 JVM 中运行。 使用 scala 命令运行 Hello 应用程序:

$ scala Hello

我们将在接下来的课程中分享更多“Hello, world”示例,因此我们暂时保留该介绍。

The Scala REPL

Scala REPL(“Read-Evaluate-Print-Loop”)是一个命令行解释器,您可以将其用作测试 Scala 代码的“操场”区域。 我们在这里很早就介绍了它,以便您可以将它与后面的代码示例一起使用。

要启动 REPL 会话,只需在您的操作系统命令行中输入 scala,您将看到如下内容:

$ scala
Welcome to Scala 2.13.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_131).
Type in expressions for evaluation. Or try :help.

scala> _

因为 REPL 是一个命令行解释器,它只是坐在那里等待你输入一些东西。 在 REPL 中,您键入 Scala 表达式以查看它们是如何工作的:

scala> val x = 1
x: Int = 1

scala> val y = x + 1
y: Int = 2

如这些示例所示,在 REPL 中键入表达式后,它会在提示后的行中显示每个表达式的结果。

Two types of variables

Scala 有两种类型的变量:

  • val is an immutable variable — like final in Java — and should be preferred
  • var creates a mutable variable, and should only be used when there is a specific reason to use it
  • Examples:
val x = 1   //immutable
var y = 0   //mutable

Declaring variable types

在 Scala 中,您通常创建变量而不声明它们的类型:

val x = 1
val s = "a string"
val p = new Person("Regina")

当您这样做时,Scala 通常可以为您推断数据类型,如以下 REPL 示例所示:

scala> val x = 1
val x: Int = 1

scala> val s = "a string"
val s: String = a string

此功能称为类型推断,它是帮助保持代码简洁的好方法。 您还可以显式声明变量的类型,但这通常不是必需的:

如您所见,该代码看起来不必要地冗长。

Control structures

这是 Scala 控制结构的快速浏览。

if/else

Scala 的 if/else 控制结构与其他语言类似:

if (test1) {
    doA()
} else if (test2) {
    doB()
} else if (test3) {
    doC()
} else {
    doD()
}

然而,与Java和许多其他语言不同的是,if/else构造返回一个值,因此,除了其他功能外,你可以将它用作一个三元操作符:

val x = if (a < b) a else b

match expressions

Scala 有一个match表达式,它最基本的用法就像一个 Java switch 语句:

val result = i match {
    case 1 => "one"
    case 2 => "two"
    case _ => "not 1 or 2"
}

匹配表达式不仅限于整数,它可以用于任何数据类型,包括布尔值:

val booleanAsString = bool match {
    case true => "true"
    case false => "false"
}

下面是一个 match 被用作方法体的例子,并且匹配许多不同的类型:

def getClassAsString(x: Any):String = x match {
    case s: String => s + " is a String"
    case i: Int => "Int"
    case f: Float => "Float"
    case l: List[_] => "List"
    case p: Person => "Person"
    case _ => "Unknown"
}

强大的匹配表达式是 Scala 的一大特色,我们将在本书后面分享更多示例。

try/catch

Scala 的 try/catch 控制结构可让您捕获异常。 它类似于Java,但其语法与匹配表达式一致:

try {
    writeToFile(text)
} catch {
    case fnfe: FileNotFoundException => println(fnfe)
    case ioe: IOException => println(ioe)
}

for loops and expressions

Scala for 循环——我们在本书中通常将其写为 for 循环——看起来像这样:

for (arg <- args) println(arg)

// "x to y" syntax
for (i <- 0 to 5) println(i)

// "x to y by" syntax
for (i <- 0 to 10 by 2) println(i)

您还可以将 yield关键字添加到 for 循环以创建产生结果的 for 表达式。 这是一个 for 表达式,它将序列 1 到 5 中的每个值加倍:

val x = for (i <- 1 to 5) yield i * 2

这是另一个遍历字符串列表的 for 表达式:

val fruits = List("apple", "banana", "lime", "orange")

val fruitLengths = for {
    f <- fruits
    if f.length > 4
} yield f.length

因为 Scala 代码通常是有意义的,我们可以想象你可以猜出这段代码是如何工作的,即使你之前从未见过 for 表达式或 Scala 列表。

while and do/while

Scala 也有 whiledo/while 循环。 这是它们的一般语法:

// while loop
while(condition) {
    statement(a)
    statement(b)
}

// do-while
do {
   statement(a)
   statement(b)
} 
while(condition)

Classes

这里是一个Scala 类的例子:

class Person(var firstName: String, var lastName: String) {
    def printFullName() = println(s"$firstName $lastName")
}

这是您使用该类的方式

val p = new Person("Julia", "Kern")
println(p.firstName)
p.lastName = "Manes"
p.printFullName()

请注意,无需创建“get”和“set”方法来访问类中的字段。

作为一个更复杂的例子,这里有一个 Pizza 类,你将在本书后面看到:

class Pizza (
    var crustSize: CrustSize,
    var crustType: CrustType,
    val toppings: ArrayBuffer[Topping]
) {
    def addTopping(t: Topping): Unit = toppings += t
    def removeTopping(t: Topping): Unit = toppings -= t
    def removeAllToppings(): Unit = toppings.clear()
}

在该代码中,ArrayBuffer 就像 Java 的 ArrayListCrustSizeCrustTypeTopping 类未显示,但您可能无需查看这些类就可以理解该代码的工作原理。

Scala methods

就像其他 OOP 语言一样,Scala 类也有方法,这就是 Scala 方法语法的样子:

def sum(a: Int, b: Int): Int = a + b
def concatenate(s1: String, s2: String): String = s1 + s2

您不必声明方法的返回类型,因此如果您愿意,可以像这样编写这两个方法是完全合法的:

def sum(a: Int, b: Int) = a + b
def concatenate(s1: String, s2: String) = s1 + s2

这就是您调用这些方法的方式:

val x = sum(1, 2)
val y = concatenate("foo", "bar")

你可以用方法做更多的事情,比如为方法参数提供默认值,但现在这是一个好的开始。

Traits

Scala 中的特性非常有趣,它们还可以让您将代码分解为小的模块化单元。 为了演示特征,这里有一个本书后面的例子。 鉴于这三个特点:

trait Speaker {
    def speak(): String  // has no body, so it’s abstract
}

trait TailWagger {
    def startTail(): Unit = println("tail is wagging")
    def stopTail(): Unit = println("tail is stopped")
}

trait Runner {
    def startRunning(): Unit = println("I’m running")
    def stopRunning(): Unit = println("Stopped running")
}

您可以创建一个 Dog 类来扩展所有这些特征,同时为 speak 方法提供行为:

class Dog(name: String) extends Speaker with TailWagger with Runner {
    def speak(): String = "Woof!"
}

类似地,这是一个 Cat 类,它展示了如何覆盖多个 trait 方法:

class Cat extends Speaker with TailWagger with Runner {
    def speak(): String = "Meow"
    override def startRunning(): Unit = println("Yeah ... I don’t run")
    override def stopRunning(): Unit = println("No need to stop")
}

如果代码是有意义的——太好了,你对trait很满意!如果没有,不要担心,我们会在后面的书中详细解释。

Collections classes

如果您是从Java学习Scala的,并且已经准备好真正投入学习Scala,那么在Scala中使用Java集合类是可能的,有些人在熟悉Scala的同时也会这样做几个星期或几个月。但是强烈建议您尽快学习基本的Scala集合类——ListListBufferVectorArrayBufferMapSet。Scala集合类的一个巨大好处是,它们提供了许多功能强大的方法,您希望尽快开始使用这些方法来简化代码。

Populating lists

有时创建填充数据的示例列表会很有帮助,Scala 提供了许多填充列表的方法。 这里仅仅是少数:

val nums = List.range(0, 10)
val nums = (1 to 10 by 2).toList
val letters = ('a' to 'f').toList
val letters = ('a' to 'f' by 2).toList

Sequence methods

虽然您可以使用许多顺序集合类 — ArrayArrayBufferVectorList等 — 让我们看一些您可以使用 List 类做什么的示例。 鉴于这两个列表

val nums = (1 to 10).toList
val names = List("joel", "ed", "chris", "maurice")

这是一个 foreach 方法:

scala> names.foreach(println)
joel
ed
chris
maurice

这是一个filter 然后是一个foreach方法

scala> nums.filter(_ < 4).foreach(println)
1
2
3

这里有一些关于Map方法的例子

scala> val doubles = nums.map(_ * 2)
doubles: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20)

scala> val capNames = names.map(_.capitalize)
capNames: List[String] = List(Joel, Ed, Chris, Maurice)

scala> val lessThanFive = nums.map(_ < 5)
lessThanFive: List[Boolean] = List(true, true, true, true, false, false, false, false, false, false)

即使没有任何解释,您也可以看到 map 是如何工作的:它将您提供的算法应用于集合中的每个元素,为每个元素返回一个新的、转换后的值。

如果您准备好了解最强大的集合方法之一,请使用 foldLeft:

scala> nums.foldLeft(0)(_ + _)
res0: Int = 55

scala> nums.foldLeft(1)(_ * _)
res1: Int = 3628800

一旦您知道 foldLeft 的第一个参数是seed value,您就可以猜测第一个示例得出nums中数字的总和,而第二个示例返回所有这些数字的乘积。

Scala 集合类有很多(很多!)更多方法可用,其中许多方法将在接下来的集合课程中演示,但希望这能让您了解它们的功能。

Tuples

元组让您可以将不同的元素集合放在一个小容器中。 一个元组可以包含 2 到 22 个值,并且所有值都可以有不同的类型。 例如,这是一个包含三种不同类型(IntDoubleString)的元组:

(11, 11.0, "Eleven")

这被称为 Tuple3,因为它包含三个元素。

元组在很多地方都很方便,例如您可能会在其他语言中使用临时类。 例如,您可以从方法返回一个元组而不是返回一个类:

def getAaplInfo(): (String, BigDecimal, Long) = {
    // get the stock symbol, price, and volume
    ("AAPL", BigDecimal(123.45), 101202303L)
}

然后你可以将方法的结果分配给一个变量:

val t = getAaplInfo()

一旦你有了一个元组变量,你就可以通过数字访问它的值,前面是下划线:

t._1
t._2
t._3

REPL 演示了访问这些字段的结果:

scala> t._1
res0: String = AAPL

scala> t._2
res1: scala.math.BigDecimal = 123.45

scala> t._3
res2: Long = 101202303

元组的值也可以使用模式匹配来提取。 在下一个示例中,元组内的字段被分配给变量symbolpricevolume

val (symbol, price, volume) = getAaplInfo()

REPL 再次演示了结果:

scala> val (symbol, price, volume) = getAaplInfo()
symbol: String = AAPL
price: scala.math.BigDecimal = 123.45
volume: Long = 101202303

当你想快速(和临时)将一些东西组合在一起的时候,元组是很好的。 如果您注意到多次使用相同的元组,那么声明一个专用的 case 类可能会很有用,例如:

case class StockInfo(symbol: String, price: BigDecimal, volume: Long)

What we haven’t shown

虽然这是 Scala 的旋风式介绍,大约有十页印刷,但还有很多东西我们还没有展示,包括:

  • 字符串和内置数字类型
  • 包装和进口
  • 如何在 Scala 中使用 Java 集合类
  • 如何在 Scala 中使用 Java 库
  • 如何构建 Scala 项目
  • 如何在 Scala 中执行单元测试
  • 如何编写 Scala shell 脚本
  • Maps、Sets 和其他集合类
  • 面向对象编程
  • 函数式编程
  • 与并发特性
  • 更多的 …
    如果您喜欢到目前为止所看到的内容,我们希望您会喜欢本书的其余部分。

以上是关于Scala学习之Scala快速入门的主要内容,如果未能解决你的问题,请参考以下文章

大数据学习之Scala语言基本语法学习36

Scala学习之函数式风格编程

Scala学习之SCALA 集合类

Scala学习之Scala中的类

Scala深入学习之函数学习

Scala学习之爬豆瓣电影