Ruby中的模式匹配从2.7引入,通过in操作符实现,匹配成功可赋值。匹配成功,整个模式匹配返回true;匹配失败,整个模式匹配返回false。
格式
Ruby中的模式匹配有2种格式:
- 单独使用in
- case in
单独使用in
值与值的匹配、值与类型的匹配、常量格式匹配、数组格式匹配、hash格式匹配
可做变量的解构赋值,失败会抛异常,如:
0 in 0 # 成功,值与值的匹配
[2, 3] in [2, 3] # 成功, 值与值的匹配
{one: 1, two: 2} in {one: 1, two: 2} # 成功,值与值的匹配
0 in Integer # 成功, 值与类型的匹配
[2, 3] in Array # 成功,值与类型的匹配
{one: 1, two: 2} in {one: 1, two: Integer} # 成功,值与类型的匹配
0 in a # 成功,常量格式匹配,a的值为0
[1, 2] in b # 成功,常量格式匹配,b的值为[1, 2]
[1, 2, 3, 4] in [one, two, *three] #成功,数组格式匹配,one值为1, two值为2,three值为[3, 4]
[0, [1, 2, 3]] in [one, [two, *three]] #成功,数组格式,one值为0, two值为1,three值为[2, 3]
{one:1, two:2, three:3} in {one:o, three:t} # 成功,hash格式匹配,o值为1,t值为3
{one: 1, two:2, three:3} in {one:, three:} # 成功,注意,key和value相同的时候,可以省略value
{one: 1, two:2, three:3} in {one:, **rest} # 成功,使用**KEY表示剩余的匹配,rest值为{two:2, three:3}
0 in 1 # 失败
[2, 3] in [1, 2] # 失败
匹配时检测类型
0 in Float => a # 匹配失败
0 in Integer => a # 成功
langs = %w[perl shell ruby]
langs in [String => a, String => b, c]
langs in [Integer => a,*other] # 匹配失败
# hash格式时可使用key: Type方式
hs = {one: 1, two: 2, three: 3}
hs in {one: Integer => a, two: Integer => b, **rest}
# a的值为1
# b的值为2
# rest的值为{three: 3}
## 注意,下面仅仅只是对value的类型进行了匹配,没有为value绑定变量
hs in {one: Integer, three: Integer}
丢弃不要的值
模式匹配时,可使用_(下划线)表示不需要的值,如:
[0, [1, 2]] in [0, [1, _] => a] # 成功,a的值为[1, 2]
[2,3,4] in [a,b,_] # 成功,a的值2,b的值为3
数组匹配与hash匹配的时候,可以使用*和**表示不需要的值,如:
[1,2,3] in [Integer, *]
{a: 1, b: 2, c: 3} in {a:, **}
case in 匹配
语法格式:
case <value>
in <pattern 1>
...
in <pattern 2>
...
else
...
end
匹配成功,执行对应的分支;匹配失败,如果有else分支,执行else分支,否则抛错误NoMatchingPatternError。
case {name: 'John', friends: [{name: 'Jane'}, {name: 'Rajesh'}]}
in name:, friends: [{name: first_friend}, *]
"matched: #{first_friend}"
else
"not matched"
end
# 匹配成功,返回 "matched: Jane"
参照上述例子,如果pattern是数组或hash的格式,那么最外层的括号可以省略。
如果pattern只包含常量,则可使用|符号表示逻辑或:只要匹配其中一个皆可成功,如:
case 0
in 0 | 1 | 2
"0 or 1 or 2"
in Hash | Array
"Hash or Array"
else
"not matched"
end
# 返回 "0 or 1 or 2"
如果使用了|符号,但是pattern中包括了变量,会抛异常SyntaxError,如:
case {a: 1, b: 2}
in {a: } | Array
"matched: #{a}"
else
"not matched"
end
# 返回 SyntaxError (illegal variable in alternative pattern (a))
case in中的pattern支持if/unless判断。
自定义对象的模式匹配
如果类实现了deconstruct()实例方法,并返回数组,则实例支持数组格式的模式匹配。
如果类实现了deconstruct_keys(keys)实例方法,并返回hash,则实例支持hash格式的模式匹配,keys表示要匹配的key数组。
class Point
def initialize(x, y)
@x, @y = x, y
end
def deconstruct
[@x, @y]
end
def deconstruct_keys(keys)
{x: @x, y: @y}
end
end
case Point.new(1, -2)
in px, Integer
"matched: #{px}"
else
"not matched"
end
# 返回 "matched: 1"
case Point.new(1, -2)
in x: 0.. => px
"matched: #{px}"
else
"not matched"
end
# 返回 "matched: 1"