scala学习手记39 - 模式匹配

在java中有switch/case这样的模式匹配语句,可以匹配的类型包括int,byte,char,short, enum,在java8又支持了字符串。

在scala中也有类似的模式匹配语句,即match-case。这个好现在之前使用过一次。scala中的match-case匹配的类型更为广泛,它是对Any类型起作用的。

来看个例子:

def activity(day: String) {
  day match {
    case "Sunday" => println("Eat, sleep, repeat... ")
    case "Saturday" => println("Hangout with friends... ")
    case "Monday" => println("...code for fun...")
    case "Friday" => println("...read a good book...")
  }
}

List("Monday", "Sunday", "Saturday").foreach {
  activity
}

执行结果:

有没有注意到,这里和java是有些不一样的,最大的区别是没有break。但是在匹配上了后,也不会继续顺序执行了。其实我一直觉得java的那个switch/case在这一点上是有些不足的,这次总算是在scala中解决了。不过这里也有些让人不爽的地方:比如没有匹配到就会抛出异常。不管怎样,这个异常是不能随意抛出的。在Java的switch/case中,是有一个default来处理那些没有匹配到的内容的。在scala中也有一个特殊的符号起到了类似的作用,就是“_”,这里的下划线是一个通配符。继续修改下上面的代码:

def activity(day: String) {
  day match {
    case "Sunday" => println("Eat, sleep, repeat... ")
    case "Saturday" => println("Hangout with friends... ")
    case "Monday" => println("...code for fun...")
    case "Friday" => println("...read a good book...")
    case _ => println("...nothing...")
  }
}

List("Monday", "Sunday", "Saturday", "Tuesday").foreach {
  activity
}

就不贴执行结果了。

需要注意的是:case表达式并不限于在match语句里使用。这里,包含case表达式的代码块就是一个简单函数值。

刚才说过match-case支持的类型是Any。在java支持的基本类型、枚举和字符串之外,scala也支持其他任意类型的实例,比如列表和元组:

def processCoordinates(input: Any) {
  input match {
    case (a, b) => printf("Processing (%d, %d)... ", a, b)
    case List("red", "blue", "white") => println("Stars and Stripes...")
    case List("red", "blue", _*) => println("colors red, blue, ... ")
    case "done" => println("done")
    case _ => null
  }
}
processCoordinates((39, -104))
processCoordinates("done")
processCoordinates(List("red", "blue", "green"))
processCoordinates (List("red", "blue", "white"))

看下执行结果:

前面有一段警告,可以不去管它。

不过代码里却是有些不足之处,其中对元组的匹配还可以更细致些,比如针对不同类型的值可以做不同的处理:

def process(input: Any) {
  input match {
  case (a: Int, b: Int) => print("Processing (int, int)... ") -
  case (a: Double, b: Double) => print("Processing (double, double)... ")
  case msg: Int if (msg > 1000000) => println("Processing int > 1000000")
  case msg: Int => print("Processing int... ")
  case msg: String => println("Processing string... ") -
  case _ => printf("Can‘t handle %s... ", input)
  }
}

process ((34.2, -159.3))
process(0)
process(1000001)
process (2.2)

代码中演示了在case语句里如何为单一的值和元组元素指定类型。除了类型之外,还可以使用卫述句(guard)。卫述句用if从句表示,在模式匹配里,对表达式求值前必须满足卫述句。

看下结果:

case的顺序很重要。Scala会自上而下地求值。所以,上面代码5和6两行是不能交换的。

##########

时间: 2025-01-09 19:57:21

scala学习手记39 - 模式匹配的相关文章

scala学习手记40 - 使用case类

前面两节我们已经多次接触过case关键字了.case关键字不仅可以用在match/case中来执行模式匹配,也可以用来修饰类.不过用case修饰的类也主要是用来做模式匹配.在上一节曾经提到过match可以是Any类型的所有类,为什么还需要使用case关键字来修饰呢?假定有这样一个场景:我们要接收和处理股票交易信息,买卖消息通常会带有一些信息,诸如股票名称.数量.把这些信息存到对象里会很方便,但是如何对他们进行模式匹配呢?这时我们就会用到case类了.case类是模式匹配器(pattern mat

scala学习手记17 - 容器和类型推断

关于scala的类型推断前面已经提到过多次.再来看一下下面这个例子: import java.util._ var list1: List[Int] = new ArrayList[Int] var list2 = new ArrayList[Int] list2 add 1 list2 add 2 var total = 0 for (index <- 0 until list2.size()) { total += list2.get(index) } println("Total i

scala学习手记8 - 自适应的默认做法

scala有一些默认做法,会让代码更简洁.更易读写,下面列出了这样几个特性: 1. 支持脚本.scala支持脚本,因此无须将所有的代码都放到类里.如果脚本可以满足需求,就将代码放到一个脚本里,无须再创建一个冗余的类. 2. return是可选的.如果没有写return关键字,方法调用会自动返回最后一个求值的表达式--如果它符合方法声明的返回值类型. 3. 分号":"是可选的.不必在每个语句的后面都写上分号,这样会使代码更简洁.如果语句太长或者包含多行的话可以换行继续写,scala能够识

scala学习手记38 - 方法命名约定和for表达式

方法命名约定 之前在学习<运算符重载>一节时曾经说过一个方法命名约定:方法的第一个字符决定了方法的优先级.现在再说另一个命名约定:如果方法以冒号(:)结尾,则调用目标是运算符后面的实例. 比如下面这个例子: class Cow { def ^(moon: Moon) = println("Cow jumped over the moon") } class Moon { def ^:(cow: Cow) = println("This cow jumped ove

scala学习手记23 - 函数值

scala的一个最主要的特性就是支持函数编程.函数是函数编程中的一等公民:函数可以作为参数传递给其他函数,可以作为其他函数的返回值,甚至可以在其它函数中嵌套.这些高阶函数称为函数值. 举一个简单的例子:从1到某个数求和.使用Java很容易实现: int sum(int max){ int result = 0; for (int i = 0; i <= max; i++) { result +=i; } return result; } 使用scala实现也没有多大区别. 现在再扩展下需求:对某

Scala类型约束代码实战及其在Spark中的应用源码解析之Scala学习笔记-39

package com.leegh.parameterization /** * @author Guohui Li */object Type_Contraints { def main(args: Array[String]): Unit = { def rocky[T](i: T)(implicit ev: T <:< java.io.Serializable) { println("Life is too short,you need Spark!") } rock

scala学习手记19 - Option类型

看到Option类型就知道这本教材应该要说那个了. 使用过guava后,应该知道guava中的Optional类的作用是什么.算了找下原始文档好了: Optional<T> is a way of replacing a nullable T reference with a non-null value. An Optional may either contain a non-null T reference (in which case we say the reference is &

scala学习手记13 - 类继承

在scala里,类继承有两点限制: 重写方法需要使用override关键字: 只有主构造函数才能往父类构造函数中传参数. 在java1.5中引入了override注解,但不强制使用.不过在scala中要想重写方法必须使用override关键字.如果确实重写了父类的方法又不使用override关键字的话,则会在编译时报错,提示没有使用override修饰符. scala的副构造函数必须调用主构造函数或是另一个副构造函数.只有在主构造函数中才能向父类的构造函数中传递数据.可以看出来主构造函数如同父类

scala学习手记10 - 访问修饰符

scala的访问修饰符有如下几个特性: 如果不指定访问修饰符,scala默认为public: 较之Java,scala对protected的定义更加严格: scala可以对可见性进行细粒度的控制. scala的默认访问修饰符 如果没有修饰符,scala会默认把类.字段.方法的访问修饰符当做public.如果要将之调整为private或protected,只需在前面添加对应的修饰符关键字即可.就如下面的程序: class Microwave{ def start() = println("star