match语句
match语句基于某个变量的值,对多个模式进行匹配并执行对应的代码块,类似于 if-elif-else 结构,但它更灵活,也更强大,不亚于直接使用 if
不过,match 的匹配比 == 运算符要严格,例如:1 和 1.0 是不匹配的。 唯一的例外是 String 和 StringName 之间的比较:比如 "hello" 与 &"hello" 会被认为是相等的
本文中的
match到if的转换仅仅是为了演示在if的语法下模拟match语句的等效判断逻辑在编写代码时,请结合实际的情况来编写if的判断逻辑,在明确知道类型的情况下,不需要使用
is_same()和类型检查
基本语法
match <值>:
<模式>:
<代码块>
<模式> when <模式防护语句>:
<代码块>
<...>模式
match语句按照从上到下的顺序进行模式匹配。匹配成功时,会执行第一个对应的代码块。执行完成后,会继续执行 match 语句后的内容。其他的模式都不会再进行匹配
字面量模式
匹配字面量,当变量的值与模式中的字面量的值相匹配时,执行对应分支
gdscriptmatch x: 1: print("We are number one!") 2: print("Two are better than one!") "test": print("Oh snap! It's a string!")等效if语句:
gdscriptif is_same(x, 1): print("We are number one!") elif is_same(x, 2): print("Two are better than one!") elif is_same(x, "test") or is_same(x, &"test"): print("Oh snap! It's a string!")为什么用
is_same而不是==? 在
match中,匹配时会根据实际的值和类型来匹配gdscriptmatch 1.0: 1: print("Match!")这段是匹配失败的!因为
match比==更严格,它要求类型和值都一致,而不是值看起来一样就行在
if中,使用==时:会进行 类型检查
比如
1 == 1.0是true但
"hello" == 1.0会报错!因为它们无法比较!
所以想安全又严格地模拟
match的行为,我们要使用is_same()函数对字符串的特殊处理
在
match中有个特殊规则:"hello"和&"hello"会被认为相等但在
if中,你不能写:gdscriptif x == "test": # 可能报错!比如 x 是 float为了安全地模拟 match 的行为,应该写成:
gdscriptif is_same(x, "test") or is_same(x, &"test"): print("Oh snap! It's a string!")或使用类型检查:
gdscriptif (x is String or x is StringName) and x == "test": print("Oh snap! It's a string!")
表达式模式
匹配表达式常量(枚举)、标识符(变量)或属性访问(
Instance.Property),变量与表达式的值相等时执行对应分支:匹配枚举
gdscriptmatch typeof(x): TYPE_FLOAT: print("float") TYPE_STRING: print("text") TYPE_ARRAY: print("array")等效if语句:
gdscriptif typeof(x) == TYPE_FLOAT: print("float") elif typeof(x) == TYPE_STRING: print("text") elif typeof(x) == TYPE_ARRAY: print("array")匹配变量
gdscriptvar x = 300 var rank_a = 100 var rank_b = 200 var rank_c = 300 match x: rank_a: print("100") rank_b: print("200") rank_c: print("300")等效if语句:
gdscriptif is_same(x, rank_a): print("100") elif is_same(x, rank_b): print("200") elif is_same(x, rank_c): print("300")匹配属性
gdscript# 假设我们有一个player对象,并且有以下属性 match x: player.hp: print("player.hp") player.level: print("player.level")等效if语句:
gdscriptif is_same(x, player.hp): print("player.hp") elif is_same(x, player.level): print("player.level")
通配符模式
通配符模式使用一个下划线 _ 来匹配任何值,相当于 match 的“保底分支”
它通常写在 match 的最后,用来处理所有其他模式都不匹配的情况,就像 if-elif-else 中的 else 一样。
match x:
1:
print("It's one!")
2:
print("It's one times two!")
_:
print("It's not 1 or 2. I don't care to be honest.")等效if语句
if is_same(x, 1):
print("It's one!")
elif is_same(x, 2):
print("It's one times two!")
else:
print("It's not 1 or 2. I don't care to be honest.")虽然 _ 是一个“通吃”的模式,但它也只能写一次,不能用多个 _ 分支
绑定模式
绑定模式和通配符 _ 很像,也是匹配所有值,不过不同的是它把匹配的值赋给一个新变量,这样我们就能在代码块中使用这个值,在数组、字典这些复杂结构中也特别实用
var x = 100
match x:
1:
print("It's one!")
2:
print("It's one times two!")
var new_var:
print("It's not 1 or 2, it's ", new_var)等效if语句:
var x = 100
if is_same(x, 1):
print("It's one!")
elif is_same(x, 2):
print("It's one times two!")
else:
var new_var = x
print("It's not 1 or 2, it's ", new_var)绑定模式的位置通常也在 match 的最后一项,作为默认分支,既起到兜底作用,又能捕获值
数组模式
数组模式可以匹配一个数组,也能够控制数组的每一元素以怎样的模式进行匹配
每个元素本身也可以是一个模式
匹配时会先检查长度,长度不一致直接失败
也可以使用
..表示开放式匹配,可以接受更长的数组每个元素之间都要用逗号隔开!
match x:
[]:
print("Empty array")
[1, 3, "test", null]:
print("Very specific array")
[var start, _, "test"]:
print("First element is ", start, ", and the last is \"test\"")
[42, ..]:
print("Open ended array")等效的 if 语句可能会稍微啰嗦点,要先确认类型,再依次判断位置和元素:
if x is Array and x.is_empty():
print("Empty array")
elif (x is Array and
x.size() == 4 and
is_same(x[0], 1) and
is_same(x[1], 3) and
(is_same(x[2], "test") or is_same(x[2], &"test")) and
x[3] == null
):
print("Very specific array")
elif (x is Array and
x.size() == 3 and
(is_same(x[2], "test") or is_same(x[2], &"test"))
):
var start = x[0]
print("First element is ", start, ", and the last is \"test\"")
elif (x is Array and
x.size() >= 1 and
is_same(x[0], 42)
):
print("Open ended array")字典模式
字典模式作用方式同数组模式,可以匹配一个字典
每一个键必须为一个常量(不能是变量)
匹配时会先检查长度,长度不一致直接失败
也可以使用
..表示开放式匹配,可以接受更长的字典每个元素之间都要用逗号隔开
值模式与键模式使用
:分隔如果只写键(不写值),表示只要求该键存在
match x:
{}:
print("Empty dict")
{"name": "Dennis"}:
print("The name is Dennis")
{"name": "Dennis", "age": var age}:
print("Dennis is ", age, " years old.")
{"name", "age"}:
print("Has a name and an age, but it's not Dennis :(")
{"key": "gdscriptdotisawesome", ..}:
print("I only checked for one entry and ignored the rest")等效if语句也同数组模式一样要啰嗦点
if x is Dictionary and x.is_empty():
print("Empty dict")
elif (x is Dictionary and
x.size() == 1 and
(x.get("name") is String or x.get("name") is StringName) and
x["name"] == "Dennis"
):
print("The name is Dennis")
elif (x is Dictionary and
x.size() == 2 and
(x.get("name") is String or x.get("name") is StringName) and
x["name"] == "Dennis" and
x.has("age")
):
var age = x["age"]
print("Dennis is ", age," years old.")
elif (x is Dictionary and
x.size() == 2 and
x.has_all(["name", "age"])
):
print("Has a name and an age, but it's not Dennis :(")
elif (x is Dictionary and
x.size() >= 1 and
(x.get("key") is String or x.get("key") is StringName) and
x["key"] == "gdscriptdotisawesome"
):
print("I only checked for one entry and ignored the rest")多重模式
你还可以使用逗号在一个 match 分支中列出多个匹配项,这些模式会被视为“或”的关系,只要其中一个匹配成功,就会执行对应的语句块
不过要注意:这些模式不能包含任何绑定变量!
match x:
1, 2, 3:
print("It's 1 - 3")
"Sword", "Splash potion", "Fist":
print("Yep, you've taken damage")等效if语句:
if (x is int and
(x == 1 or x == 2 or x == 3)
):
print("It's 1 - 3")
elif ((x is String or x is StringName) and
(x == "Sword" or x == "Splash potion" or x == "Fist")
):
print("Yep, you've taken damage")模式防护
在 match 中,你可以为某些模式添加一个额外的判断条件,这就是「模式防护」(用 when 关键词指定)
当匹配到了某个模式后,只有在防护条件为 true 时才会执行对应的语句块。 如果条件不满足,就会继续检查下一个分支
这在处理多个类似结构但细节不同的情况时,特别实用
match point:
[0, 0]:
print("Origin")
[_, 0]:
print("Point on X-axis")
[0, _]:
print("Point on Y-axis")
[var x, var y] when y == x:
print("Point on line y = x")
[var x, var y] when y == -x:
print("Point on line y = -x")
[var x, var y]:
print("Point (%s, %s)" % [x, y])when 条件只有在模式匹配成功之后才会被执行。
一旦进入某个分支,后面的分支将不再被检查,和 if-elif-else 一样。
等效的 if 语句会稍显繁琐,像这样:
if (point is Array and
point.size() == 2 and
is_same(point[0], 0) and
is_same(point[1], 0)
):
print("Origin")
elif (point is Array and
point.size() == 2 and
is_same(point[1], 0)
):
print("Point on X-axis")
elif (point is Array and
point.size() == 2 and
is_same(point[0], 0)
):
print("Point on Y-axis")
elif (point is Array and
point.size() == 2 and
is_same(point[1], point[0])
):
print("Point on line y = x")
elif (point is Array and
point.size() == 2 and
is_same(point[1], -point[0])
):
print("Point on line y = -x")
elif (point is Array and
point.size() == 2
):
print("Point (%s, %s)" % [point[0], point[1]])练习
数值判断
写一个
match语句,打印以下内容:如果
x是 0,输出"Zero";如果
x是 1 或 2,输出"One or Two";否则输出
"Something else"。
gdscriptvar x = 2枚举类型匹配
假设你有一个枚举:
gdscriptenum State { IDLE, WALKING, RUNNING }写一个
match语句,根据枚举变量的值打印状态文字(例如"Currently Idle")。字符串/名字匹配
写一个
match语句,当weapon是"Sword"、"Bow"或"Wand"时输出对应武器名,否则输出"Unarmed"。gdscriptvar weapon = "Bow"数组结构判断
编写一个
match语句,根据数组结构输出信息:[]→ 输出"Empty"[1, 2, 3]→ 输出"123!"[x, _, "GDScript"]→ 输出"Starts with x, ends with GDScript"[42, ..]→ 输出"Starts with 42"其他 → 输出
"Unrecognized pattern"
gdscriptvar x = [1, 2, 3]字典模式匹配
写一个
match语句来检查字典中是否包含"type"和"value"键:如果
type是"attack"并且value > 10,输出"Powerful attack";如果
type是"heal",输出"Healing...";否则输出
"Unknown action"。
gdscriptvar action = {"type": "attack", "value": 12}模式防护练习
匹配一个表示二维坐标的数组:
point = [x, y]:原点
[0, 0]→ 输出"Origin"任意横坐标为 0 的点 → 输出
"On Y-axis"任意纵坐标为 0 的点 → 输出
"On X-axis"如果在 y = x 直线上 → 输出
"Diagdscriptnal line!"其他情况输出具体坐标