模式匹配
模式与项式有着相同的结构,但它们还可以包含变量。变量名都以大写字母开头。
模式示例:
- {A, a, 12, [12,34|{a}]}
- {A, B, 23}
- {x, {X_1}, 12, My_cats_age}
- []
以上的A、B、X_1和My_cats_age都是变量。
模式匹配为变量赋值提供了基本的机制。被赋值后,变量便被绑定——否则便是未绑定变量。给变量赋值的动作称作“绑定”。变量一旦被绑定便不可更改。这种变量属性被称为一次性绑定或单次赋值。这种属性与传统命令式语言的破坏性赋值[2]相反。
如果一个模式与一个项式在结构上同构,且在模式中任一位置出现的原子数据类型也都在项式的相应位置上出现,则称他们它们相互匹配。如果模式中包含未绑定变量,则该变量在匹配过程中将被绑定到项式中相应的元素。如果在模式中相同的变量多次出现,则项式中对应位置的元素也必须相同。
模式匹配在以下情况下发生:
- 计算形如Lhs=Rhs的表达式时
- 调用函数时
- 在case和receive原语中对指定模式进行匹配时
Pattern = Expression
表达式Pattern=Expression将致使Expression被求值,并将其结果与Pattern进行匹配。匹配要么成功要么失败。若匹配成功则Pattern中的所有(未绑定)变量都将被绑定。
以下我们将假设模式匹配总是成功。对失败的处理将在第??章详细讨论。
示例:
- {A, B} = {12, apple}
匹配成功后建立绑定关系A→12[3]和B→apple。
- {C, [Head|Tail]} = {{222, man}, [a,b,c]}
匹配成功后建立绑定关系C→{222,man}、Head→a和Tail→[b,c]。
- [{person, Name, Age, _}|T] =
- [{person, fred, 22, male},
- {person, susan, 19, female}, ...]
匹配成功后建立绑定关系T→[{person,susan,19,female},…]}、Name→fred和Age→22。在最后一个例子中我们利用了写作“_”的匿名变量——在语法上需要一个变量出现,但我们又不关心该变量的值的时候便可以使用匿名变量。
当一个变量在一个模式中多次出现时,只有被匹配的对应元素的值都相同时匹配才会成功。因此,举例来说,{A,foo,A}={123,foo,123}将成功匹配,并将A绑定到123,然而{A,foo,A}={123,foo,abc}就会失败,因为我们不能将A同时绑定到123和abc。
“=”是一个右结合的中缀运算符。因此A=B=C=D将被解析为A=(B=(C=D))。这种用法可能只有在{A,B}=X=…这样的构造中才有用,这时我们可以同时获悉表达式的值及其组成部分。表达式Lhs=Rhs的值被定义为Rhs。
函数调用中的模式匹配
Erlang通过模式匹配来提供选择和控制流程。例如,程序2.1定义了一个函数classify_day/1,当调用参数为saturday或sunday时返回weekEnd,否则返回weekDay 。
程序 2.1
- -module(dates).
- -export([classify_day/1]).
- classify_day(saturday) -> weekEnd;
- classify_day(sunday) -> weekEnd;
- classify_day(_) -> weekDay.
进行函数求值时,会将函数的参数与函数定义中出现的模式一一进行匹配。一旦发现一个成功的匹配,“->”之后的符号便被求值,因此:
- > dates:classify_day(saturday).
- weekEnd
- > dates:classify_day(friday).
- weekDay
如果所有的子句都不匹配,则函数调用失败(失败将引发第??章描述的错误捕捉机制)。
当执行流程进入一个函数的某个子句时,描述该子句的模式所包含的变量将被绑定。因此,举例来说,对程序1.3的math3:area({square,5})进行求值将致使变量Side被绑定到5。