Scala 样本类(case class)和模式匹配(match)学习
程序员文章站
2022-06-14 17:34:40
...
[size=medium][color=red][b]样本类(case clas)和模式匹配(pattern matching),在编写规范的、无封装数据结构时会用到的构件。[/b][/color][/size]对于树型递归数据尤其有用。
[size=large][color=red][b]一、简单例子:[/b][/color][/size]
[size=medium][color=blue][b]样本类:添加了case的类便是样本类。[/b][/color][/size][b]这种修饰符可以让Scala编译器自动为这个类添加一些语法上的便捷设定。[/b]如下:
添加与类名一致的工厂方法。也就是说,可以写成Var("x")来构造Var对象。
[size=medium][color=blue][b]样本类参数列表中的所有参数隐式获得了val前缀,因此它被当作字段维护。[/b][/color][/size]
编译器为这个类添加了方法toString,hashCode和equals等方法。
[size=large][color=red][b]模式匹配: [/b][/color][/size]
[size=medium][color=red][b]match对应Java里的switch,但是写在选择器表达式之后。[/b][/color][/size]即: 选择器 match {备选项}。
[b]一个模式匹配包含了一系列备选项,每个都开始于关键字case。每个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式。[/b]
match表达式通过以代码编写的先后次序尝试每个模式来完成计算。类似于UnOp("-" , UnOp("-" , e))这种形式的,是构造器模式匹配。
[color=red][b]match与switch的比较:匹配表达式可以被看作Java风格Switch的泛化。[/b][/color]
但有三点不同:
1.match是Scala的表达式,始终以值作为结果;
[color=red][b]2.Scala的备选项表达式永远不会“掉到”下一个case;[/b][/color]
3.如果没有模式匹配,MatchError异常会被抛出。这意味着必须始终确信所有的情况都考虑到了,或者至少添加一个默认情况什么都不做。如 case _ =>
[size=large][color=orange][b]二、模式的种类[/b][/color][/size]
[color=red][b]通配模式:case _ => 。表示默认的全匹配备选项。通配模式还可以用来忽略对象中不关心的部分。[/b][/color]如:case BinOp(_,_,_) => XXX,则表示不关心二元操作符的元素是什么,只是检查是否为二元操作符
[color=red][b]常量模式 :[/b][/color]仅匹配自身。任何字面量都可以用作常量。包括String类型。另外,任何的val或单例对象也可以被用作常量。如,单例对象Nil是只匹配空列表的模式。
[color=blue][b]变量模式 :[/b][/color]变量模式类似于通配符,可以匹配任何对象。不同点在于,Scala把变量绑定在匹配的对象上。之后就可以使用这个变量操作对象。如:
有一个注意点:Scala使用了一个简单的文字规则来区分是一个常量还是一个变量:用小写字母开始的简单名被当作是模式变量。
[size=medium][color=red][b]构造器模式:[/b][/color][/size]它的存在使得模式匹配真正变得强大。它由名称及若干括号之内的模式构成。如BinOp("+" , e , Number(0))。
[size=medium][color=red][b]序列模式:[/b][/color][/size]可以像匹配样本类那样匹配List或Array这样的序列类型。同样的语法现在可以指定模式内任意数量的元素。如:
[color=red][b]如果想匹配一个不指定长度的序列,可以指定_*作为模式的最后元素。[/b][/color]它能匹配序列中0到任意数量的元素。
[size=medium][color=red][b]元组模式:[/b][/color][/size]
[size=medium][color=red][b]类型模式:[/b][/color][/size]可以把它当做类型测试和类型转换的简易替代。例如:
使用:
[color=red][b]另:在Scala里类型测试和转换的代码很冗长,因为不建议这么做。[/b][/color]
[size=medium][color=blue][b]模式守卫:模式变量仅允许在模式中出现一次。[/b][/color][/size]如:
1 case BinOp("+" , x , x ) => BinOp("*" , x , Number(2))
这种写法就会出错。可以改成: case BinOp("+" , x , y ) if x == y => BinOp("*" , x , Number(2))
模式守卫接在模式之后,开始于if。[color=red][size=medium][b]守卫可以是任意的引用模式中变量的布尔表达式。如果存在模式守卫,那么只有在守卫返回true的时候匹配才成功。[/b][/size][/color]
[size=large][color=red][b]一、简单例子:[/b][/color][/size]
abstract class Expr
case class Var(name:String) extends Expr
case class Number(num:Double) extends Expr
case class UnOp(operator : String , arg : Expr) extends Expr
case class BinOp(operator : String , left : Expr , right : Expr) extends Expr
[size=medium][color=blue][b]样本类:添加了case的类便是样本类。[/b][/color][/size][b]这种修饰符可以让Scala编译器自动为这个类添加一些语法上的便捷设定。[/b]如下:
添加与类名一致的工厂方法。也就是说,可以写成Var("x")来构造Var对象。
[size=medium][color=blue][b]样本类参数列表中的所有参数隐式获得了val前缀,因此它被当作字段维护。[/b][/color][/size]
编译器为这个类添加了方法toString,hashCode和equals等方法。
[size=large][color=red][b]模式匹配: [/b][/color][/size]
def simplifyTop(expr : Expr) : Expr = expr match{
case UnOp("-" , UnOp("-" , e)) => e
case BinOp("+" , e , Number(0)) => e
case BinOp("*" , e , Number(1)) => e
case _ => expr
}
[size=medium][color=red][b]match对应Java里的switch,但是写在选择器表达式之后。[/b][/color][/size]即: 选择器 match {备选项}。
[b]一个模式匹配包含了一系列备选项,每个都开始于关键字case。每个备选项都包含了一个模式及一到多个表达式。箭头符号 => 隔开了模式和表达式。[/b]
match表达式通过以代码编写的先后次序尝试每个模式来完成计算。类似于UnOp("-" , UnOp("-" , e))这种形式的,是构造器模式匹配。
[color=red][b]match与switch的比较:匹配表达式可以被看作Java风格Switch的泛化。[/b][/color]
但有三点不同:
1.match是Scala的表达式,始终以值作为结果;
[color=red][b]2.Scala的备选项表达式永远不会“掉到”下一个case;[/b][/color]
3.如果没有模式匹配,MatchError异常会被抛出。这意味着必须始终确信所有的情况都考虑到了,或者至少添加一个默认情况什么都不做。如 case _ =>
[size=large][color=orange][b]二、模式的种类[/b][/color][/size]
[color=red][b]通配模式:case _ => 。表示默认的全匹配备选项。通配模式还可以用来忽略对象中不关心的部分。[/b][/color]如:case BinOp(_,_,_) => XXX,则表示不关心二元操作符的元素是什么,只是检查是否为二元操作符
[color=red][b]常量模式 :[/b][/color]仅匹配自身。任何字面量都可以用作常量。包括String类型。另外,任何的val或单例对象也可以被用作常量。如,单例对象Nil是只匹配空列表的模式。
[color=blue][b]变量模式 :[/b][/color]变量模式类似于通配符,可以匹配任何对象。不同点在于,Scala把变量绑定在匹配的对象上。之后就可以使用这个变量操作对象。如:
expr match{
case 0 => "zero"
case somethingElse => "not zero" + somethingElse
}
有一个注意点:Scala使用了一个简单的文字规则来区分是一个常量还是一个变量:用小写字母开始的简单名被当作是模式变量。
[size=medium][color=red][b]构造器模式:[/b][/color][/size]它的存在使得模式匹配真正变得强大。它由名称及若干括号之内的模式构成。如BinOp("+" , e , Number(0))。
[size=medium][color=red][b]序列模式:[/b][/color][/size]可以像匹配样本类那样匹配List或Array这样的序列类型。同样的语法现在可以指定模式内任意数量的元素。如:
expr match{
case List(0 , _ , _ ) => println("found it")
case _ =>
}
[color=red][b]如果想匹配一个不指定长度的序列,可以指定_*作为模式的最后元素。[/b][/color]它能匹配序列中0到任意数量的元素。
[size=medium][color=red][b]元组模式:[/b][/color][/size]
def tupleDemo(expr : Any) =
expr match {
case (a , b, c) => println("matched " + a + b + c)
case _ =>
}
[size=medium][color=red][b]类型模式:[/b][/color][/size]可以把它当做类型测试和类型转换的简易替代。例如:
def generalSize(x : Any) = x match{
case s : String => s.length
case m : Map[_ , _] => m.size
case _ => 1
}
使用:
scala> generalSize(Map(1 -> 'a' , 2 -> 'b'))
res15 : Int = 2
[color=red][b]另:在Scala里类型测试和转换的代码很冗长,因为不建议这么做。[/b][/color]
[size=medium][color=blue][b]模式守卫:模式变量仅允许在模式中出现一次。[/b][/color][/size]如:
1 case BinOp("+" , x , x ) => BinOp("*" , x , Number(2))
这种写法就会出错。可以改成: case BinOp("+" , x , y ) if x == y => BinOp("*" , x , Number(2))
模式守卫接在模式之后,开始于if。[color=red][size=medium][b]守卫可以是任意的引用模式中变量的布尔表达式。如果存在模式守卫,那么只有在守卫返回true的时候匹配才成功。[/b][/size][/color]