Scala:match表达式、break和continue

开发 后端
本文节选自Martin Odersky,Lex Spoon和Bill Venners所著,Regular翻译的《Programming in Scala》的第七章。Scala是一种针对 JVM 将函数和面向对象技术组合在一起的编程语言。

match表达式

Scala的匹配表达式允许你在许多可选项:alternative中做选择,就好象其它语言中的switch语句。通常说来match表达式可以让你使用任意的模式:pattern做选择。通用的模式可以稍等再说。目前,只要考虑使用match在若干可选项中做选择。

51CTO编辑推荐:Scala编程语言专题

作为例子,代码7.14里的脚本从参数列表读入食物名然后打印食物配料。match表达式检查参数列表的第一个参数firstArg。如果是字串"salt",就打印"pepper",如果是"chips",就打印"salsa",如此递推。缺省情况用下划线(_)说明,这是常用在Scala里作为占位符表示完全不清楚的值的通配符。

  1. val firstArg = if (args.length > 0) args(0) else ""
  2. firstArg match {
  3. case "salt" => println("pepper")
  4. case "chips" => println("salsa")
  5. case "eggs" => println("bacon")
  6. case _ => println("huh?")
  7. }
代码 7.14 有副作用的match表达式

与Java的switch语句比,匹配表达式还有一些重要的差别。其中之一是任何种类的常量,或其他什么东西,都能用作Scala里的case,而不只是Java的case语句里面的整数类型和枚举常量。在这个例子里,可选项是字串。另一个区别是在每个可选项的最后并没有break。取而代之,break是隐含的,不会有从一个可选项转到另一个里面去的情况。这通常把代码变短了,并且避免了一些错误的根源,因为程序员不再因为疏忽在选项里转来转去。

然而,与Java的switch相比最显著的差别,或许是match表达式也能产生值。在前一个例子里,match表达式的每个可选项打印输出一个值。只生成值而不是打印也可以一样做到,展示在代码7.15中。match表达式产生的值储存在friend变量里。这除了能让代码变得更短之外(至少减少了几个指令),还解开了两个不相干的关注点:首先选择食物名,其次打印它。

  1. val firstArg = if (!args.isEmpty) args(0) else ""
  2. val friend =
  3. firstArg match {
  4. case "salt" => "pepper"
  5. case "chips" => "salsa"
  6. case "eggs" => "bacon"
  7. case _ => "huh?"
  8. }
  9. println(friend)

代码 7.15 生成值的match表达式

离开break和continue

你可能注意到了这里没有提到过break和continue。Scala去掉了这些命令因为他们与函数式文本,下一章会谈到这个特征,啮合得不好。continue在while循环中的意思很清楚,但是在函数式文本中表示什么呢?虽然Scala既支持指令式风格也支持函数式风格,但在这点上它略微倾向于函数式编程从而换得在语言上的简洁性。尽管如此,请不要着急。有许多不用break和continue的编程方式,如果你能有效利用函数式文本,就能比原来的代码写得更短。

最简单的方式是用if替换每个every和用布尔变量替换每个break。布尔变量指代是否包含它的while循环应该继续。比如说,假设你正搜索一个参数列表去查找以“.scala”结尾但不以连号开头的字串。Java里你可以——如果你很喜欢while循环,break和continue——如此写:

  1. int i = 0; // 在Java中……
  2. boolean foundIt = false;
  3. while (i < args.length) {
  4. if (args[i].startsWith("-"))
  5. {
  6. i = i + 1;
  7. continue;
  8. }
  9. if (args[i].endsWith(".scala")) {
  10. foundIt = true;
  11. break;
  12. }
  13. i = i + 1;
  14. }

如果要字面直译成Scala的代码,代之以执行一个if然后continue,你可以写一个if环绕while余下的全部内容。要去掉break,你可以增加一个布尔变量提示是否继续做下去,不过在这里你可以复用foundIt。使用这两个技巧,代码就可以像代码7.16这样完成了:

  1. var i = 0
  2. var foundIt = false
  3. while (i < args.length && !foundIt) {
  4. if (!args(i).startsWith(""))
  5. {
  6. if (args(i).endsWith(".scala"))
  7. foundIt = true
  8. }
  9. i = i + 1
  10. }

代码 7.16 不带break或continue的循环

这个版本与原来的Java代码非常像。所有的主要段落仍然存在并保持原顺序。有两个可重新赋值的变量及一个while循环。循环内有个i是否小于args.length的测试,然后检查"-",然后检查".scala"。

如果要去掉代码7.16里面的var,你可以尝试的一种方式是用递归函数重写循环。比方说,你可以定义带一个整数值做输入的searchFrom函数,向前搜索,并返回想要的参数的索引。采用这种技巧的代码看上去会像展示在代码7.17中这样的:

  1. def searchFrom(i: Int): Int =
  2. if (i >= args.length) -1// 不要越过最后一个参数
  3. else if (args(i).startsWith("-")) searchFrom(i + 1)// 跳过选项
  4. else if (args(i).endsWith(".scala")) i // 找到!
  5. else searchFrom(i + 1) // 继续找
  6. val i = searchFrom(0)

代码 7.17 不用var做循环的递归替代方法

代码7.17的版本提供了一个能够看得懂的名字说明这个函数在做什么,它用递归替代了循环。每个continue都被带有i + 1做参数的递归调用替换掉,有效地跳转到下一个整数。许多人都发现当他们开始使用递归后,这种编程风格更易于理解。

注意

Scala编译器不会实际对代码7.17展示的代码生成递归函数。因为所有的递归调用都在尾调用:tail-call位置,编译器会产生出与while循环类似的代码。每个递归调用将被实现为回到函数开始位置的跳转。

【相关阅读】

  1. 学习Scala:使用try-catch表达式处理异常
  2. Scala中的for表达式:枚举的“瑞士军刀”
  3. Scala中的if表达式和while循环
  4. 学习Scala的重载方法和隐式转换
  5. Scala的四种标识符构成方式

责任编辑:book05 来源: Artima
相关推荐

2009-07-21 14:03:00

Scalaif表达式while循环

2020-07-19 07:44:16

PHP 8.0Match语法

2012-07-18 09:45:32

Java 8ScalaLambda

2009-08-20 14:57:00

C#正则表达式

2009-09-16 14:22:44

preg_match正

2009-07-21 14:16:18

Scalafor表达式

2009-07-21 14:30:38

Scalatry-catch

2018-09-27 15:25:08

正则表达式前端

2009-12-29 10:22:51

Scala 2.8

2014-01-05 17:41:09

PostgreSQL表达式

2010-11-16 14:53:02

Oracle游标表达式

2009-09-17 09:09:50

Lambda表达式Linq查询

2009-05-22 11:38:55

PHPpreg_match_正则表达式

2009-11-26 18:00:46

PHP正则表达式pre

2009-11-27 13:53:43

PHP函数preg_m

2023-11-02 18:45:00

Rust编程表达式

2009-05-22 09:48:07

表达式树泛型委托.NET

2024-03-25 13:46:12

C#Lambda编程

2023-11-01 13:32:42

Go代码

2009-08-10 17:11:34

.NET 3.5扩展方Lambda表达式
点赞
收藏

51CTO技术栈公众号