Skip to content

match语句

match语句基于某个变量的值,对多个模式进行匹配并执行对应的代码块,类似于 if-elif-else 结构,但它更灵活,也更强大,不亚于直接使用 if

不过,match 的匹配比 == 运算符要严格,例如:11.0 是不匹配的。 唯一的例外是 StringStringName 之间的比较:比如 "hello"&"hello" 会被认为是相等的

本文中的matchif的转换仅仅是为了演示在if的语法下模拟match语句的等效判断逻辑

在编写代码时,请结合实际的情况来编写if的判断逻辑,在明确知道类型的情况下,不需要使用is_same()和类型检查

基本语法

gdscript
match <>:
    <模式>:
        <代码块>
    <模式> when <模式防护语句>:
        <代码块>
    <...>

模式

match语句按照从上到下的顺序进行模式匹配。匹配成功时,会执行第一个对应的代码块。执行完成后,会继续执行 match 语句后的内容。其他的模式都不会再进行匹配

字面量模式

  • 匹配字面量,当变量的值与模式中的字面量的值相匹配时,执行对应分支

    gdscript
    match x:
        1:
            print("We are number one!")
        2:
            print("Two are better than one!")
        "test":
            print("Oh snap! It's a string!")

    等效if语句:

    gdscript
    if 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中,匹配时会根据实际的值和类型来匹配

    gdscript
    match 1.0:
        1:
            print("Match!")

    这段是匹配失败的!因为 match== 更严格,它要求类型和值都一致,而不是值看起来一样就行

    if 中,使用 == 时:

    • 会进行 类型检查

      • 比如 1 == 1.0true

      • "hello" == 1.0 会报错!因为它们无法比较!

    所以想安全又严格地模拟 match 的行为,我们要使用is_same() 函数

    对字符串的特殊处理

    match中有个特殊规则:

    "hello"&"hello" 会被认为相等

    但在 if 中,你不能写:

    gdscript
    if x == "test": # 可能报错!比如 x 是 float

    为了安全地模拟 match 的行为,应该写成:

    gdscript
    if is_same(x, "test") or is_same(x, &"test"):
        print("Oh snap! It's a string!")

    或使用类型检查:

    gdscript
    if (x is String or x is StringName) and x == "test":
        print("Oh snap! It's a string!")

表达式模式

  • 匹配表达式常量(枚举)、标识符(变量)或属性访问(Instance.Property),变量与表达式的值相等时执行对应分支:

    • 匹配枚举

      gdscript
      match typeof(x):
          TYPE_FLOAT:
              print("float")
          TYPE_STRING:
              print("text")
          TYPE_ARRAY:
              print("array")

      等效if语句:

      gdscript
      if typeof(x) == TYPE_FLOAT:
          print("float")
      elif typeof(x) == TYPE_STRING:
          print("text")
      elif typeof(x) == TYPE_ARRAY:
          print("array")
    • 匹配变量

      gdscript
      var 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语句:

      gdscript
      if 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语句:

      gdscript
      if is_same(x, player.hp):
          print("player.hp")
      elif is_same(x, player.level):
          print("player.level")

通配符模式

通配符模式使用一个下划线 _ 来匹配任何值,相当于 match 的“保底分支”

它通常写在 match 的最后,用来处理所有其他模式都不匹配的情况,就像 if-elif-else 中的 else 一样。

gdscript
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语句

gdscript
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.")

虽然 _ 是一个“通吃”的模式,但它也只能写一次,不能用多个 _ 分支

绑定模式

绑定模式和通配符 _ 很像,也是匹配所有值,不过不同的是它把匹配的值赋给一个新变量,这样我们就能在代码块中使用这个值,在数组、字典这些复杂结构中也特别实用

gdscript
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语句:

gdscript
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 的最后一项,作为默认分支,既起到兜底作用,又能捕获值

数组模式

数组模式可以匹配一个数组,也能够控制数组的每一元素以怎样的模式进行匹配

  1. 每个元素本身也可以是一个模式

  2. 匹配时会先检查长度,长度不一致直接失败

  3. 也可以使用 .. 表示开放式匹配,可以接受更长的数组

  4. 每个元素之间都要用逗号隔开!

sql
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 语句可能会稍微啰嗦点,要先确认类型,再依次判断位置和元素:

gdscript
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")

字典模式

字典模式作用方式同数组模式,可以匹配一个字典

  1. 每一个键必须为一个常量(不能是变量)

  2. 匹配时会先检查长度,长度不一致直接失败

  3. 也可以使用..表示开放式匹配,可以接受更长的字典

  4. 每个元素之间都要用逗号隔开

  5. 值模式与键模式使用:分隔

  6. 如果只写键(不写值),表示只要求该键存在

gdscript
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语句也同数组模式一样要啰嗦点

gdscript
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 分支中列出多个匹配项,这些模式会被视为“或”的关系,只要其中一个匹配成功,就会执行对应的语句块

不过要注意:这些模式不能包含任何绑定变量!

gdscript
match x:
    1, 2, 3:
            print("It's 1 - 3")
    "Sword", "Splash potion", "Fist":
            print("Yep, you've taken damage")

等效if语句:

gdscript
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 时才会执行对应的语句块。 如果条件不满足,就会继续检查下一个分支

这在处理多个类似结构但细节不同的情况时,特别实用

gdscript
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 语句会稍显繁琐,像这样:

gdscript
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]])

练习

  1. 数值判断

    写一个 match 语句,打印以下内容:

    • 如果 x 是 0,输出 "Zero"

    • 如果 x 是 1 或 2,输出 "One or Two"

    • 否则输出 "Something else"

    gdscript
    var x = 2
  2. 枚举类型匹配

    假设你有一个枚举:

    gdscript
    enum State { IDLE, WALKING, RUNNING }

    写一个 match 语句,根据枚举变量的值打印状态文字(例如 "Currently Idle")。

  3. 字符串/名字匹配

    写一个 match 语句,当 weapon"Sword""Bow""Wand" 时输出对应武器名,否则输出 "Unarmed"

    gdscript
    var weapon = "Bow"
  4. 数组结构判断

    编写一个 match 语句,根据数组结构输出信息:

    • [] → 输出 "Empty"

    • [1, 2, 3] → 输出 "123!"

    • [x, _, "GDScript"] → 输出 "Starts with x, ends with GDScript"

    • [42, ..] → 输出 "Starts with 42"

    • 其他 → 输出 "Unrecognized pattern"

    gdscript
    var x = [1, 2, 3]
  5. 字典模式匹配

    写一个 match 语句来检查字典中是否包含 "type""value" 键:

    • 如果 type"attack" 并且 value > 10,输出 "Powerful attack"

    • 如果 type"heal",输出 "Healing..."

    • 否则输出 "Unknown action"

    gdscript
    var action = {"type": "attack", "value": 12}
  6. 模式防护练习

    匹配一个表示二维坐标的数组:point = [x, y]

    • 原点 [0, 0] → 输出 "Origin"

    • 任意横坐标为 0 的点 → 输出 "On Y-axis"

    • 任意纵坐标为 0 的点 → 输出 "On X-axis"

    • 如果在 y = x 直线上 → 输出 "Diagdscriptnal line!"

    • 其他情况输出具体坐标