规则
首先我们需要对规则有一个清晰的认识。
对于一个规则,如果不将其应用于表达式,那么就没什么意义。而只有当其应用于表达式时(ReplaceAll or /.),才有意义,它表示了一种替换规则,例如Rule[x, 2],即x->2,应用于表达式后,表示将表达式中的所有x替换为2。例如
x^2+3/.x->2
out:7
注意区分规则和函数,将规则应用于表达式与函数应用于列表不同,函数操作带@,而规则是点'.'。
定义规则
第一种方式:使用Rule函数
第二种方式:直接使用箭头
应用规则
ReplaceAll -- /.
但ReplaceAll有一个问题,它只能进行一轮替代,比如
x + 2 y /. {x -> y, y -> z}
out:
y + 2 z
可以看到,x替换后的y并不会应用第二个规则。如果想要完全替代(multiple replacements),则可以使用下面的函数:
ReplaceRepeated -- //.
从执行过程上解释差别:规则替代过程是从表达式左边一直扫向右边,然后使用/.后,软件在从左到右扫描表达式的时候,发现匹配规则,就会直接替换,例如上面的例子,首先扫到x,发现x匹配规则中的x->y,就将其替换为y,然后就直接扫描下一项了,直接扫到最后一项。但是//.在扫描到最后一项后,会重新开始扫描,直到没有匹配的项为止。
我们可以通过MaxIterations选项告诉ReplaceRepeated扫描的次数,例如:
ReplaceRepeated[1/(1 + x), x -> 1/(1 + x), MaxIterations -> 4]
out:
1/(1 + 1/(1 + 1/(1 + 1/(1 + 1/(1 + x)))))
mathematica中的规则
比如对方程求解后,例如
Solve[x^2 - 2 x == 0, x]
out:
{{x -> 0}, {x -> 2}}
我们可以发现输出类型为规则,因此,如果我们想把输出结果转换为列表,那么我们可以使用规则替换:
x /. %
从上面我们也可以发现,对一个表达式应用一个规则列表,那么会输出相应的列表,列表元素分别对应着列表中的规则。
由此我们可以发现,规则在mathematica中是十分常见的,规则也是模式的基础,而模式就是mathematica最为突出的特点之一。
模式匹配
上面提到的规则替换就是将表达式对应的值替换,而很多场景里,我们需要替换的不是某个特定的值,而是对一种结构进行替换,例如我想对指数为2的项替换为乘法,即x\^2->2x, y\^2->2y,这时候我们的规则里就不能只是特定的值了,而是要表示这种^2的形式,这其实就是模式。
我们要搞清楚模式匹配的含义,比如MatchQ[x^2, x^2],并不是指两个表达式的值相等,而是它们的属性相同,匹配的是属性,比如说2的属性包括整数、偶数、正数等等,我们匹配的其中的某个属性或者某些属性,这就是模式匹配。从mathematica的角度说,是根据FullForm进行匹配的。
而在模式匹配中,上面的MatchQ[x\^2, x\^2]这种匹配是匹配了所有属性,即只有前后相等才会返回True,你换成MatchQ[x^2, y\^2]就会返回False。在更多的场景下,我们需要匹配的是其中的部分属性,这就引入了模式中最重要的符号——'_',它的含义可以是anything,类似于通配符。引入当使用MatchQ[x^2, _],返回便是True,因为这里的'_'可以是x^2。
我们常常需要将匹配到的部分转换成其他形式,因此我们需要给匹配到的部分起一个名字,在mathematica里就可以表示为一个变量名+下划线,比如'a_',然后a就代表匹配到的部分,这样就可以利用a进行规则的替换,比如:
x^2 + x^3 /. a_^2 -> 2 a
out:
2 x + x^3
如果我们想要匹配表达式的头部,可以使用_Head,比如_Power,MatchQ[x^2, _Power],将会返回True。
再看这个例子:
MatchQ[x^2, x^_Integer]
out: True
MatchQ[x^2, x^_Real]
out: False
可以看出,在mathematica内部,并不认为2是一个Real。这说明了mathematica对整数和实数有着不同的处理机制。
带条件的模式
在很多场景下,我们需要对我们设置的模式加上一定的条件或者满足一定的函数,即将一定的函数加在前面的模式上返回True才进行匹配。
后面的条件可以采用纯函数或者内置的函数,比如:(注意纯函数要用括号括起来)
MatchQ[5, _Integer?(# > 3 &)]
out:True
?的应用是很灵活的,并不一定要放到最后,我们需要对哪个"_"进行匹配,就需要放到其后面。比如:
MatchQ[x^2, _?(# == x &)^_]
out:True
MatchQ[x^2, _^_?(# == x &)]
out:False
从上面的例子可以看出,如果模式中出现了几个"",则判断条件在哪个"\"后面,就对其进行条件判断。注意,前面提到的a_给匹配内容命名是无法应用到条件函数里的,它是应用在规则替换中的
模式在列表中的应用
上面提到的都是对单个表达式进行替换,下面将对多个表达式和列表的操作。
在mathematica(1)中提到,如果我们需要创建一个子列表,可以使用Select函数,按照一定的函数进行筛选,函数可以为匿名函数。既然模式也可以匹配出我们需要的内容,那能不能通过模式对列表匹配出子列表呢,当然是可以的。这就需要用到Cases函数。在前面的内容中我们可以发现,模式不仅可以匹配结构,还可以通过加以条件实现类似于函数筛选的功能,从这种程度上来说,Cases的应用比Select的应用更为广泛。在我看来,Select[list, f]完全是可以用Cases[list, _?(f)]来替代的。
在前面我们讲到了规则替换,因此,我们也可以对模式匹配到的内容进行替换,从这个角度上说,Cases的应用将更为广泛,即对带有一定条件的模式引用一定的规则,比如:a_?(f) -> rhs
Cases语法:

示例:
Cases[{{1, 2}, {4, 3}, {5, 1}}, {_, _?(# > 1 &)}]
out:{{1, 2}, {4, 3}} 匹配第二项大于1的列表
Cases[{{1, 2}, {4, 3}, {5, 1}}, {a_, b_?(# > 1 &)} -> {a, x}]
out:{{1, x}, {4, x}} 匹配第二项大于1的列表,并将第二项替换为x
列表中的多个表达式
看下面这个例子:
MatchQ[{x^2, x^3}, {_}]
out:False
MatchQ[{{x^2, x^3}}, {_}]
out:True
从上例中我们可以得出,_并不能处理列表中的多个表达式,但能够代替列表,因为一个列表相当于一个表达式,并非是多个表达式。引入一个新的通配符——"__",双下划线。双下划线表示一个非空的表达式序列,在这个概念上,将上例改为:
MatchQ[{x^2, x^3}, {__}]
out:True
可以看到输出结果为True。但注意,不能匹配空的表达式序列。而有方法可以把空的表达式序列也囊括进去吗?当然是有的,即"___",三个下划线,它表示任意数量的表达式序列,包括空的
MatchQ[{}, {_}]
out:False
MatchQ[{}, {__}]
out:False
MatchQ[{}, {___}]
out:True
这些通配符常常应用于对序列的匹配,看下面的例子,我们对{1,2,3,4}的所有随机序列,提取出2不在第一位,4在2的后面的所有情况:
Cases[Permutations[{1, 2, 3, 4}], {__, 2, ___, 4, ___}]
out:
{{1, 2, 3, 4}, {1, 2, 4, 3}, {1, 3, 2, 4}, {3, 1, 2, 4}, {3, 2, 1, 4}, {3, 2, 4, 1}}
2不在第一位,说明2前面要有一个或者多个表达式,可以用双下划线表示,而4在2的后面,说明2和4的中间有0、1或者多个表达式,可以用三个下划线表示,而4的后面也是这样,用三个下划线表示。
练习题
在前50000个素数中,得到数字中包含2006的素数序列。
步骤:
1、找到pattern
我们往往需要将问题转换为一个数字列表或者表达式列表的匹配问题,在这里我们可以使用对数字拆分的方式将其转化为数字列表。我们需要的模式是包含2007,首先我们对数进行拆分,得到的是一个表达式序列,例如对120079进行拆分,得到{1, 2, 0, 0, 7, 9},因此,模式可以为:
MatchQ[IntegerDigits[120079], {___, 2, 0, 0, 7, ___}]
out:True
经过测试,可以发现我们的模式没有问题。
接下来我们要考虑如何获得素数序列。
利用内置的素数函数Prime
IntegerDigits /@ Prime /@ Range[50000]
2、使用Cases
模式匹配和规则替换
Cases[IntegerDigits /@ Prime /@ Range[50000], {___, 2, 0, 0, 7, ___}]
我们还需要将其转化为整数,这是可以使用函数,因为是对列表中的每一个元素都要应用此函数。
FromDigits /@ Cases[IntegerDigits /@ Prime /@ Range[50000], {___, 2, 0, 0, 7, ___}]