Scala学习日记 Day1

本文介绍了Scala中的程序控制流,包括条件表达式、代码块、用户输入输出和循环体。讲解了Scala如何处理条件判断、代码块的执行以及循环的使用,特别强调了Scala中没有Java的switch语句,以及for循环的高级特性如笛卡尔积和generator的应用。同时,文中探讨了Scala中如何实现类似break和continue的控制流,并提供了示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

工作如何繁忙,生活如何糟心,至少求知的这一刻是我的…

前言

基于Scala 2.12.14。学的《Scala for the Impatient》,记录每天的笔记,有些名词看的英文,翻得未必准确。
适用对象:Java程序员。尽可能在短时间内建立起Java到Scala的转换,做到能看能写。实际上在学习一门语言,应当逐步抛弃掉固有成见,才能发现其中乐趣。人生苦短,别用Java:(逃。

今日重点

了解Scala中的程序控制流,包含有:

  • 条件表达式
  • 代码块
  • 用户输入输出
  • 循环体

条件表达式

在Scala中,条件表达式后往往跟着赋值语句,判定条件的语法和Java是一致的,但紧接着给出条件表达式的值,等价于"foo == bar ? v0: v1"三元运算:

if (0 < x) 1 else -1

因此可以写成:

// 推荐使用这种方式,v可以按val初始化
val v = if (0 < x) 1 else -1

等价于:

// 这就和Java的语法一样了
// v只能被申明为var
if (0 < x) v = 1 else v = -1

在Scala中,分支的表达式的值是有类型的:

// 因为所有分支的表达式的值的类型都是Int,所以该表达式的类型是Int
if (0 < x) 1 else -1
// 表达式的值类型有Int有String,该表达式的类型是这两个类的共同父类,也就是Any
if (0 < x) "str" else -1

省略else的语法示例如下:

if (0 < x) 1

如果期望特定分支没有值呢?

// ()实际对应了Scala中的Unit,可以看作是Java中的void
// 上边忽略else语句,可等价于:
if (0 < x) 1 else ()

Scala中是没有Java中的switch分支的,不是重要语法。

交互式命令行是一次读取一行并执行,试试这个:

scala> if (0 < x) 1
res1: AnyVal = 1

scala> else if (x < 0) -1 else -2
<console>:1: error: illegal start of definition
else if (x < 0) -1 else -2
^

想想Java是怎么做的?

scala> val x = 1
x: Int = 1

scala> if (0 < x) {
      1 } else if (x < 0) -1 else -2
res1: Int = 1

是的,使用{}指明这是一个语句块,并且让这个语句块跨行。接着我们继续看代码块。

代码块

在Java中,我们常使用";"语法代表一条语句的结束,但在Scala中并非必须,只有在一行有多条语句时才有必要分隔。

if (x < 0) foo += 1; bar -= 1

没有闭合的语句可以让编译器/即时交互命令行读取跨行的表达式。没写完的表达式:

var sum = v0 + v1 *
  v2 - v3

if (0 < x) { // 未闭合
  sum = v0 + v1
  avg = sum / 2
} // 闭合

可以看到Scala的代码块和Java很相似,但Scala的代码块是有值的,并可被赋值给变量:

val foo = {
  val bar = 1
  // 最后一个值即foo的值
  1
}

要注意,赋值/初始化语句是没有值的,看一个示例:

val foo = {
  val bar = 1
  // foo = () 即 Unit
}

因为赋值语句没有值,因此链式的赋值语句是错误的,即使Java中可以这么使用:

val x = y = 1
// 一步步拆开看:
// y = 1的值是()
// x = ()

用户输入输出

打印输出

java中常见的print和println都有,只不过是我们在scala中直接可调用这两个函数:

print("hello ")
println("world")
// c-like
printf("Hello %s, %d%n", "scala", 2021)
// 也可以,$取相应的变量值,像perl, shell...
// 但Scala更高级,$还可以带表达式
print(f"Hello, $scala! Now is ${year + 1 - 0.5 * 2}%4.1f.%n")

推荐使用f插值器的方式,即print(f"string"),它可以支持表达式,支持c-like的占位符,更重要的是,它是类型安全的,如果使用了特定类型的占位符,但其值不是这种类型,那么在编译期就会抛错。如%f,但表达式的值不是数值。
在Scala中,插值器还有s和raw。s插值器允许使用字符串表达式;raw表达式则是对值进行自动转义。

scala>  raw"\n is a newline"
res8: String = \n is a newline

scala>  "\n is a newline"
res9: String =
"
 is a newline"

scala> s"$$$year"
res10: String = $2021
scala> s"$$year"
res11: String = $year
scala> s"$year"
res12: String = 2021
// $取值,但$$会转义

用户输入

读取输入,可以使用scala.io.StdIn类中的方法,包含有:

  • readInt

  • readDoube

  • readByte

  • readShort

  • readLonf

  • readFloat

  • readBoolean

  • readChar

  • readLine

    方法名望文生义。

scala> import io.StdIn._
import io.StdIn._
scala> val x = readInt() // << 11
x: Int = 11
scala> val y = readLine("y = ") // << 22
y = y: String = 22 // readLine读取的是String

循环体

while

和Javaer常用的while是一样的,不用示例了吧。

for

for循环和Javaer常用的for(initialize; test; update)语法不一致,但和for(value: iterable)是很像的,Scala的语法是:

for(i <- 1 to n)
  v += 1
// 一步步拆解开:
// 1 to n,还记得Day0的内容吗?实际上是由RichInt提供的方法,它将返回[1, n]的序列
// <-,则是Scala表示遍历将表达式的值,但值应为Scala中的集合。示例中的是Range。

Scala中for语法总结出也就是for(i <- expr)。

字符串怎么遍历呢?

val s = "hello"
for (i <- 0 to s.length - 1)
  print(s(i))
// 也可以如此
for (c <- s)
  print(c)

break和continue

在循环体控制流中重要的break和continue语法决定了我们编写程序的方式,但在Scala中是没有这两种控制语法的。有替代方法吗?——有的:

// break替代
import util.control.Breaks._
breakable {
  for(...) {
    if (...) break // break是函数,它将跳到breakable标记的语句块外。
  }
}
// break到这里执行

但对于性能要求比较高的场景,不适合使用这种方法,因为它实际上是通过抛出异常并捕获实现的。JVM中抛出异常会发生什么呢?自然是完整地收集异常栈信息,这可就太慢啦。
还有别的方式吗?自然是使用布尔值决定循环制流的走向。还有种是通过嵌套函数的方式,将循环封装成一个函数,在特定条件下return。

Scala循环的高级特性

笛卡尔积

Scala允许在循环体中应用多个generator,也就是多个variable <- expression:

for (i <- 1 to 3; j <- 1 to 3) print(f"${10 * i + j}%3d")
// 11 12 13 21 22 23 31 32 33

generator应用条件表达式

每个generator都可以应用一个条件表达式:

for (i <- 1 to 3; j <- 1 to 3 if i != j) print(f"${10 * i + j}%3d")
// 12 13 21 23 31 32

generator应用变量

generator可以使用变量,实际上也不算特性,循环本身就可以使用变量,只不过是在某个generator中定义的变量的作用域是支持到其他generator使用的:

for (i <- 1 to 3; from = 4 - i; j <- from to 3) print(f"${10 * i + j}%3d")
// 13 22 23 31 32 33

循环生成集合

循环体的表达式是可以有值的,可以通过yield关键字构造表达式的值为集合:

scala> for (i <- 1 to 10) yield i % 3
res17: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 0, 1, 2, 0, 1, 2, 0, 1)

生成的集合中的类型取决于第一个generator:

scala>   for (ch <- "hello"; i <- 0 to 1) yield (ch + i).toChar
res20: String = hieflmlmop

scala>   for (i <- 0 to 1; ch <- "hello") yield (ch + i).toChar
res21: scala.collection.immutable.IndexedSeq[Char] = Vector(h, e, l, l, o, i, f, m, m, p)

茴的四种写法

for循环结构可跨行:

for {
    i <- 1 to 3
    j = 3 - i
    }
// 等价于
for (i <- 1 to 3; j = 3 - i)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值