.. _pattern-matching: ================ 模式匹配 ================ 在计算机编程领域,有一个称为模式匹配的概念。 这种技术允许我们获取一个复杂的值,例如数组或变体,并将其与一组模式进行比较。 如果该值符合某个模式,则匹配过程将继续,我们可以从该值中提取特定值。 这是一个强大的工具,可以提高我们的代码的可读性和效率。 在本节中,我们将探索在 Daslang 中使用模式匹配的不同方式。 在 Daslang 中,模式匹配是通过 `daslib/match` 模块中的宏实现的。 ^^^^^^^^^^^^^^^^^^^^ Enumeration Matching ^^^^^^^^^^^^^^^^^^^^ Daslang 支持对枚举进行模式匹配,这允许您将枚举的值与特定模式匹配。 您可以使用此功能来简化代码,因为无需多个 if-else 语句或 switch 语句。 要匹配 Daslang 中的枚举,请使用 match 关键字,后跟枚举值和一系列 if 语句,每个语句表示要匹配的模式。如果找到匹配项,则执行相应的代码块。 例:: enum Color { Black Red Green Blue } def enum_match (color:Color) { match ( color ) { if ( Color Black ) { return 0 } if ( Color Red ) { return 1 } if ( _ ) { return -1 } } } 在此示例中,enum_match 函数将 Color 枚举值作为参数,并根据匹配的模式返回一个值。 if Color Black 语句与 Black 枚举值匹配,if Color Red 语句与 Red 枚举值匹配,if _ 语句是与尚未显式匹配的任何其他枚举值匹配的 catch-all。 ^^^^^^^^^^^^^^^^^^^^ 匹配变体 ^^^^^^^^^^^^^^^^^^^^ 可以使用 match 语句匹配 Daslang 中的变体。 变体是一种可区分联合类型,它包含多个可能的值之一,每个值都是不同的类型。 在此示例中,IF 变体有两个可能的值:int 类型的 i 和 float 类型的 f。 variant_as_match 函数将 IF 类型的值作为参数,并对其进行匹配以确定其类型。 if _ as i 语句匹配任何值,并将其分配给声明的变量 i。 同样,if _ as f 语句匹配任何值并将其分配给声明的变量 f。 最后的 if _ 语句匹配任何剩余值,并返回 “anything”。 例:: variant IF { i : int f : float } def variant_as_match (v:IF) { match ( v ) { if ( _ as i ) { return "int" } if ( _ as f ) { return "float" } if ( _ ) { return "anything" } } } 在 Daslang 中,可以使用用于创建新变体的相同语法来匹配变体。 下面是一个示例:: def variant_match (v : IF) { match ( v ) { if ( IF(i=$v(i)) ) { return 1 } if ( IF(f=$v(f)) ) { return 2 } if ( _ ) { return 0 } } } 在上面的示例中,函数 variant_match 采用 IF 类型的变体 v。第一种情况如果包含 i 并将 i 的值绑定到变量 i,则匹配 v。 在本例中,该函数返回 1。第二种情况如果包含 f 并将 f 的值绑定到变量 f,则匹配 v。在这种情况下,该函数返回 2。T 最后一个 case 匹配与前两个 case 不匹配的任何内容,并返回 0。 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 在模式匹配中声明变量 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 在 Daslang 中,您可以在模式匹配语句中声明变量,包括变体匹配。 要声明变量,请使用语法 $v(decl),其中 decl 是要声明的变量的名称。 然后,为声明的变量分配匹配模式的值。 此功能不仅限于变体匹配,可以在 Daslang 中的任何模式匹配语句中使用。 在此示例中,if $v(as_int)语句在保存整数并声明变量 as_int 以存储该值时匹配 variant 值。同样,$v(as_float) 语句在保存浮点值并声明变量 as_float 来存储该值时匹配 variant 值。 示例:: variant IF { i : int f : float } def variant_as_match (v:IF) { match ( v ) { if ( $v(as_int) as i ) { return as_int } if ( $v(as_float) as f ) { return as_float } if ( _ ) { return None } } } ^^^^^^^^^^^^^^^^^^^^ 匹配结构体 ^^^^^^^^^^^^^^^^^^^^ Daslang 支持使用 match 语句匹配结构。 结构体是一种复合数据类型,它将不同数据类型的变量分组到一个名称下。 在此示例中,Foo 结构具有一个 int 类型的成员 a。 struct_match 函数采用 Foo 类型的参数,并将其与各种模式进行匹配。 如果 [[Foo a=13]] 匹配 a 等于 13 的 Foo 结构,则第一个匹配项,如果匹配成功,则返回 0。 如果 [[Foo a=$v(anyA)]] 匹配任何 Foo 结构并将其成员绑定到声明的变量 anyA,则第二次匹配。 如果成功,则此匹配返回 anyA 的值。 示例:: struct Foo { a : int } def struct_match (f:Foo) { match ( f ) { if ( Foo(a=13) ) { return 0 } if ( Foo(a=$v(anyA)) ) { return anyA } } } ^^^^^^^^^^^^^^^^^^^^ 使用守卫 ^^^^^^^^^^^^^^^^^^^^ Daslang 支持在其模式匹配机制中使用守卫。 守卫是除了成功的模式匹配之外还必须满足的条件。 在此示例中,AB 结构有两个 int 类型的成员 a 和 b。 guards_match 函数采用 AB 类型的参数,并将其与各种模式进行匹配。 如果 [[AB a=$v(a), b=$v(b)]] && (b > a) 匹配 AB 结构并将其 a 和 b 成员分别绑定到声明的变量 a 和 b,则第一次匹配。 还必须满足守卫条件 b > a 才能使此匹配成功。如果此匹配成功,该函数将返回一个字符串,指示 b 大于 a。 如果 [[AB a=$v(a), b=$v(b)]] 匹配任何 AB 结构,并将其 a 和 b 成员分别绑定到声明的变量 a 和 b,则第二次匹配。 通过警卫对比赛没有额外的限制。如果此匹配成功,该函数将返回一个字符串,指示 b 小于或等于 a。 例:: struct AB { a, b : int } def guards_match (ab:AB) { match ( ab ) { if ( AB(a=$v(a), b=$v(b)) && (b > a) ) { return "{b} > {a}" } if ( AB(a=$v(a), b=$v(b)) ) { return "{b} <= {a}" } } } ^^^^^^^^^^^^^^^^^^^^ 元组匹配 ^^^^^^^^^^^^^^^^^^^^ 在 Daslang 中匹配元组是通过双方括号完成的,并使用与创建新元组相同的语法。 必须指定 Tuples 的类型,或者可以使用 auto 来指示自动类型推理。 下面是一个演示 Daslang 中的元组匹配的示例:: def tuple_match ( A : tuple ) { match ( A ) { if (1,_,"3") { return 1 } if (13,...) { // starts with 13 return 2 } if (...,"13") { // ends with "13" return 3 } if (2,...,"2") { // starts with 2, ends with "2" return 4 } if ( _ ) { return 0 } } } 在此示例中,tuple 类型的元组 A 作为参数传递给函数 tuple_match。 该函数使用 match 语句来匹配元组 A 中的不同模式。 match 语句中的 if 子句使用双方括号指定要匹配的模式。 要匹配的第一个模式是 (1,_,"3")。 该模式匹配以值 1 开头,后跟任何值,以字符串 “3” 结尾的元组。 模式中的 _ 符号表示可以在 Tuples 中的该位置匹配任何值。 要匹配的第二个模式是 (13,...(,它匹配以值 13 开头的元组。 这...符号表示在值 13 之后可以匹配任意数量的值。 要匹配的第三个模式是 (...,"13"),它匹配以字符串 “13” 结尾的元组。 这...符号表示可以在字符串 “13” 之前匹配任意数量的值。 要匹配的第四个模式是 (2,...,"2"),它匹配以值 2 开头并以字符串 “2” 结尾的元组。 如果没有任何模式匹配,则执行 _ 子句,函数返回 0。 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 匹配 Static 数组 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Daslang 中的静态数组可以使用双方括号语法进行匹配,类似于元组。 此外,静态数组必须指定其类型,或者可以使用 auto 关键字自动推断类型。 下面是一个匹配 int[3] 类型的静态数组的示例:: def static_array_match ( A : int[3] ) { match ( A ) { if ( fixed_array($v(a),$v(b),$v(c)) && (a+b+c)==6 ) { // 总共 3 个元素,总和为 6 return 1 } if ( fixed_array(0,...) ) { // 以 0 开头 return 0 } if ( fixed_array(..,13) ) { // 以 13 结尾 return 2 } if ( fixed_array(12,...,12) ) { // 以 12 开头和结尾 return 3 } if ( _ ) { return -1 } } } 在此示例中,函数 static_array_match 采用 int[3] 类型的参数,该参数是一个包含三个整数的静态数组。 match 语句使用双方括号语法来匹配输入数组 A 的不同模式。 第一种情况,fixed_array($v(a),$v(b),$v(c)) && (a+b+c)==6,匹配一个数组,其中三个元素的总和等于6。 匹配的元素使用 $v 语法分配给变量 a、b 和 c。 接下来的三种情况分别匹配以 0 开头、以 13 结尾、以 12 开头和结尾的数组。 这...语法 用于匹配两者之间的任何元素。 最后,_ 大小写匹配任何与任何其他大小写都不匹配的数组,在本例中返回 -1。 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 动态数组匹配 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 动态数组用于存储可在运行时更改的值集合。 在 Daslang 中,可以使用与元组类似的语法将动态数组与模式匹配,但增加了对数组中元素数的检查。 下面是一个对动态整数数组进行匹配的示例:: def dynamic_array_match ( A : array ) { match ( A ) { if ( [$v(a),$v(b),$v(c)] && (a+b+c)==6 ) { // 总共 3 个元素,总和为 6 return 1 } if ( [0,0,0,...] ) { // 前 3 个为 0 return 0 } if ( [...,1,2] ) { // 以 1,2 结尾 return 2 } if ( [0,1;...,2,3] ) { // 以 0,1 开头,以 2,3 结尾 return 3 } if ( _ ) { return -1 } } } 在上面的代码中,dynamic_array_match 函数将一个动态整数数组作为参数。 然后,match 语句尝试将数组中的元素与一系列模式进行匹配。 第一种模式如果 [$v(a),$v(b),$v(c)] && (a+b+c)==6 匹配包含三个元素的数组,并且这些元素的总和为 6。 $v 语法用于匹配和捕获数组中元素的值。然后,可以在条件 (a+b+c)==6 中使用捕获的值。 如果 [0,0,0,...] 匹配以三个 0 开头的数组,则第二种模式。这 ... 语法用于匹配数组中的任何剩余元素。 如果 [...,1,2] 匹配以元素 1 和 2 结尾的数组,则第三种模式。 如果 [0,1,...,2,3] 匹配以元素 0 和 1 开头并以元素 2 和 3 结尾的数组,则第四种模式匹配。 如果 _ 匹配与前面的任何模式不匹配的任何数组,则为最终模式。 请务必注意,动态数组中的元素数必须与模式中的元素数匹配,匹配才会成功。 ^^^^^^^^^^^^^^^^^^^^ 匹配表达式 ^^^^^^^^^^^^^^^^^^^^ 在 Daslang 中,match 表达式允许您重复使用模式中较早声明的变量,以匹配模式中较晚的表达式。 以下示例演示了如何使用 match 表达式来检查整数数组是否按升序排列:: def ascending_array_match ( A : int[3] ) { match ( A ) { if [$v(x),match_expr(x+1),match_expr(x+2)] ) { return true } if ( _ ) { return false } } } 在此示例中,数组的第一个元素与 x 匹配。然后,分别使用 match_expr 和表达式 x+1 和 x+2 匹配接下来的两个元素。 如果所有三个元素都匹配,则函数返回 true。如果没有匹配项,该函数将返回 false。 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 匹配 ||表达式 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 在 Daslang 中,您可以使用 || 表达式 按选项的显示顺序匹配它们。当您想要根据多个条件匹配变体时,这非常有用。 以下是使用 ||表达:: struct Bar { a : int b : float } def or_match ( B:Bar ) { match ( B ) { if ( Bar(a=1,b=$v(b)) || Bar(a=2,b=$v(b)) ) { return b if ( _ ) { return 0.0 } } } 在此示例中,函数 or_match 采用 Bar 类型的变体 B,并使用 ||表达。 当 a 的值为 1 且 b 被捕获为变量时,第一个选项匹配。 当 a 的值为 2 且 b 被捕获为变量时,第二个选项匹配。 如果这些选项中的任何一个匹配,则返回 b 的值。如果两个选项都不匹配,则返回 0.0。 需要注意的是,对于 || 表达式 才能正常工作,则语句的两端都必须声明相同的变量。 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [match_as_is] 结构注释 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Daslang 中的 [match_as_is] 结构注释允许您对不同类型的结构执行模式匹配。 这允许在单个模式匹配表达式中匹配不同类型的结构体 只要已为匹配类型实现了必要的 is 和 as 运算符。 下面是如何使用 [match_as_is] 结构注释的示例:: [match_as_is] struct CmdMove : Cmd { override rtti = "CmdMove" x : float y : float } 在此示例中,结构 CmdMove 标有 [match_as_is] 注释,允许它参与模式匹配:: def operator is CmdMove ( cmd:Cmd ) { return cmd.rtti=="CmdMove" } def operator is CmdMove ( anything ) { return false } def operator as CmdMove ( cmd:Cmd ==const ) : CmdMove const& { assert(cmd.rtti=="CmdMove") unsafe { return reinterpret cmd } } def operator as CmdMove ( var cmd:Cmd ==const ) : CmdMove& { assert(cmd.rtti=="CmdMove") unsafe { return reinterpret cmd } } def operator as CmdMove ( anything ) { panic("Cannot cast to CmdMove") return default } def matching_as_and_is (cmd:Cmd) { match ( cmd ) { if CmdMove(x=$v(x), y=$v(y)) ) { return x + y } if ( _ ) { return 0. } } } 在此示例中,已为 CmdMove 结构实现了必要的 is 和 as 运算符,以允许其参与模式匹配。is 运算符用于确定类型的兼容性,as 运算符用于执行实际的类型转换。 在 matching_as_and_is 函数中,使用CmdMove(x=$v(x),y=$v(y))模式将 cmd 与 CmdMove 结构进行匹配。如果匹配成功,则提取 x 和 y 的值并返回总和。如果匹配不成功,则匹配 catch-all _ 大小写,并返回 0.0。 **注意** 仅当已为匹配类型实现必要的 is 和 as 运算符时,[match_as_is] 结构注释才有效。在上面的示例中,已经为 CmdMove 结构实现了必要的 is 和 as 运算符,以允许它参与模式匹配。 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [match_copy] 结构注释 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Daslang 中的 [match_copy] 结构注释允许您对不同类型的结构执行模式匹配。 这允许在单个模式匹配表达式中匹配不同类型的结构体 只要已为匹配类型实现了必要的 match_copy 函数。 下面是如何使用 [match_copy] 结构注释的示例:: [match_copy] struct CmdLocate : Cmd { override rtti = "CmdLocate" x : float y : float z : float } 在此示例中,结构 CmdLocate 标有 [match_copy] 注释,允许它参与模式匹配。 match_copy 函数用于匹配不同类型的结构。下面是 CmdLocate 结构的 match_copy 函数的实现示例:: def match_copy ( var cmdm:CmdLocate; cmd:Cmd ) { if ( cmd.rtti != "CmdLocate" ) { return false } unsafe { cmdm = reinterpret cmd } return true } 在此示例中,match_copy 函数采用两个参数:CmdLocate 类型的 cmdm 和 Cmd 类型的 cmd。 此函数的用途是确定 cmd 参数是否为 CmdLocate 类型。 如果是,该函数将使用 reinterpret 执行到 CmdLocate 的类型强制转换,并将结果分配给 cmdm。 然后,该函数返回 true 以指示类型转换成功。如果 cmd 参数不是 CmdLocate 类型,则函数返回 false。 以下是如何在 matching_copy 函数中使用 match_copy 函数的示例:: def matching_copy ( cmd:Cmd ) { match ( cmd ) { if ( CmdLocate(x=$v(x), y=$v(y), z=$v(z)) ) { return x + y + z } if ( _ ) { return 0. } } } 在此示例中,matching_copy 函数采用 Cmd 类型的单个参数 cmd。此函数对 cmd 参数执行类型匹配作以确定其类型。 如果 cmd 参数的类型为 CmdLocate,则该函数返回其 x、y 和 z 字段的值之和。如果 cmd 参数是任何其他类型,则函数返回 0。 **注意** 仅当为匹配类型实现了必要的 match_copy 函数时,[match_copy] 结构注释才有效。 在上面的示例中,已为 CmdLocate 结构实现了必要的 match_copy 函数,以允许它参与模式匹配。 ^^^^^^^^^^^^^^^^^^^^ 静态匹配 ^^^^^^^^^^^^^^^^^^^^ 静态匹配是一种匹配泛型表达式 Daslang 的方法。它的工作方式与常规匹配类似,但有一个关键区别: 当 match 表达式和模式之间存在类型不匹配时,匹配将在编译时被忽略,而不是编译错误。 这使得静态匹配对于泛型函数来说是稳健的。 静态匹配的语法如下:: static_match ( match_expression ) { if ( pattern_1 ) { return result_1 } if ( pattern_2 ) { return result_2 } ... if ( _ ) { return result_default } } 这里, match_expression 是要与 patterns 匹配的表达式。每个模式都是match_expression将与之进行比较的值或表达式。 如果 match_expression 匹配其中一个模式,则返回相应的结果。如果没有任何模式匹配,则将返回 result_default。 如果 pattern 无法匹配,则将被忽略。 这是一个例子:: enum Color { red green blue } def enum_static_match ( color, blah ) static_match ( color ) { if ( Color red ) { return 0 } if ( match_expr(blah) ) { return 1 } if ( _ ) { return -1 } } } 在此示例中,颜色与枚举值 red、green 和 blue 进行匹配。如果匹配表达式 color 等于枚举值 red,则返回 0。 如果匹配表达式 color 等于 blah 的值,则将返回 1。如果没有任何模式匹配,将返回 -1。 **注意** match_expr 用于将 blah 与匹配表达式颜色匹配,而不是直接将 blah 与枚举值匹配。 如果 color 不是 Color,则第一次匹配将失败。如果 blah 不是 Color,则第二个匹配将失败。但该函数将始终编译。 ^^^^^^^^^^ match_type ^^^^^^^^^^ Daslang 中的 match_type 子表达式允许您根据表达式的类型执行模式匹配。 它在 static_match 语句中用于指定要匹配的表达式类型。 match_type 的语法如下:: if ( match_type(type, expr) ) { // code to run if match is successful } 其中 Type 是要匹配的类型,expr 是要匹配的表达式。 下面是如何使用 match_type 子表达式的示例:: def static_match_by_type (what) { static_match ( what ) { if ( match_type(type,$v(expr)) ) { return expr } if ( _ ) { return -1 } } } 在此示例中,要匹配的表达式是什么。如果 what 的类型为 int,则将其分配给变量 $v 并返回表达式 expr。如果 what 不是 int 类型,则匹配落入 catch-all _ 大小写,并返回 -1。 **注意** match_type 子表达式仅匹配类型,不匹配的值将被忽略。这与常规模式匹配相反,在常规模式匹配中,type 和 value 必须匹配才能成功。 ^^^^^^^^^^^^^^^^^^^^^^^^^ 多重匹配 ^^^^^^^^^^^^^^^^^^^^^^^^^ 在 Daslang 中,您可以使用 multi_match 功能来匹配单个表达式中的多个值。当您想要根据多个不同的条件匹配值时,这非常有用。 以下是使用 multi_match 功能的示例:: def multi_match_test ( a:int ) { var text = "{a}" multi_match ( a ) { if ( 0 ) { text += " zero" } if ( 1 ) { text += " one" } if ( 2 ) { text += " two" } if ( $v(a) && (a % 2 == 0) && (a!=0) ) { text += " even" } if ( $v(a) && (a % 2 == 1) ) { text += " odd" } } return text } 在此示例中,函数 multi_match_test 采用整数值 a 并使用 multi_match 功能对其进行匹配。 前 3 个选项分别在 a 等于 0、1 或 2 时匹配。 当 a 不等于 0 且为偶数时,第四个选项匹配。 当 a 为奇数时,第五个选项匹配。变量文本将根据匹配条件进行更新。 最终结果将作为 text 的字符串表示形式返回。 请务必注意,multi_match 功能允许在单个表达式中匹配多个条件。 与使用多个 match 和 if 语句相比,这使得代码更简洁、更易于阅读。 使用常规匹配的相同示例如下所示:: def multi_match_test ( a:int ) { var text = "{a}" match ( a ) { if ( 0 ) { text += " zero" } } match ( a ) { if ( 1 ) { text += " one" } } match ( a ) { if ( 2 ) { text += " two" } } match ( a ) { if ( $v(a) && (a % 2 == 0) && (a!=0) ) { text += " even" } } match ( a ) { if ( $v(a) && (a % 2 == 1) ) { text += " odd" } } return text } `static_multi_match` 是 `multi_match` 的变体,可与 `static_match` 一起使用。