Elixir 簡明筆記(三)--- 模式匹配

函數(shù)式語言的一大特點就是使用模式匹配來做條件判斷。Elixir甚至可以使用模式匹配來替代 if else 這些傳統(tǒng)編程語言的控制結(jié)構(gòu)。模式匹配對于Elixir至關(guān)重要,那么到底什么是模式匹配呢?

賦值號?等號?模式匹配號?

稍微有一點編程經(jīng)驗的人都會知道“=”這個符號是賦值符號,左邊表示一個變量,右邊是一個表達式或者值,將右邊的值或表達式求值的結(jié)果賦予左邊的變量。剛開始學編程的時候,都會強調(diào)賦值號與數(shù)學中的等號的差別。

可是在elixir中,你又得忘掉編程中的賦值號,重新拾起遺忘的數(shù)學等號。因為Elixir并沒有賦值的概念,“=”也不是賦值號。Elixir中的“=”號是一個模式匹配符號。

變量綁定

=號是一個模式匹配符號,匹配成功將會進行綁定。請看下面的例子:

iex(1)> x = 1
1
iex(2)> x
1
iex(3)>

上述代碼中,在x=1這個表達式中,左邊的x是一個變量(實際上很多函數(shù)式語言的變量都是不可變的量,elixir的“變量”也是不變的)。右邊的 1 是一個數(shù)值。表達式的含義是進行變量綁定。將1的值綁定給變量x,所以輸入x的時候,對x求值等于1

也許你不以為然,變量綁定和賦值有什么差別呢?那么請看下面的語句:

iex(1)> x = 1
1
iex(2)> x
1
iex(3)> 1 = x
1
iex(4)> x
1
iex(5)>

常見的傳統(tǒng)編程語言可不會出現(xiàn)1=x這樣的表達式。實際上,Elixir中的=和數(shù)學上的等號概念類似,左邊等于右邊,右邊等于左邊,那么表達式就不會報錯。當然,對于變量,可以重新綁定:

iex(1)> x = 1
1
iex(2)> x
1
iex(5)> x = 2
2
iex(6)> x
2

這個過程其實并沒有改變變量x的值,而只是對x進行了重新的綁定。效果類似改變了值。

模式,匹配

既然知道了=是模式匹配符號,而不是等號,那上面為什么在說變量綁定?Elixir中的模式匹配英文是Pattern Match。可以理解為通過某個模式進行匹配,就像使用正則表達式這樣的pattern對字符串進行匹配一樣。一旦匹配成功,就會對匹配的項進行變量綁定。從匹配到綁定整個過程稱之為模式匹配。有點拗口,貴在理解啦。

模式匹配,就必須先知道模式。=符號左邊表示一個模式(pattern),右邊則是一個表達式,或者一個數(shù)值。所謂的模式,也可以是一個數(shù)值,可以是一個變量,甚至也可以一些基本數(shù)據(jù)結(jié)構(gòu),例如列表(List),元組(Tuple),類字典(Dicionary)等。只要等號左右兩邊的數(shù)據(jù)類型和結(jié)構(gòu)相同,則可以匹配。

從最簡單的開始吧。前面的例子,我們見識了變量的模式匹配

iex(8)> a = 1
1

模式a和數(shù)值1匹配,匹配成功,將右邊的數(shù)值和左邊的模式變量a進行綁定。

iex(8)> a = 1
1
iex(9)> 1 = a
1

1=a,這個模式匹配表示含義是,左邊的模式是數(shù)字1。右邊是一個變量,該變量已經(jīng)綁定了數(shù)值1,因此對a變量(表達式)求值,得到值1。表達式實際含義等價于1 = 1。右邊的值1和左邊的數(shù)字1值相等,匹配成功,然后進行變量綁定,可是右邊的模式是數(shù)字1不是變量,就忽略了綁定過程。整個模式匹配成功。

在看下面的代碼:

iex(8)> a = 1
1
iex(9)> 1 = a
1
iex(10)> 2 = a
** (MatchError) no match of right hand side value: 1
iex(10)>

表達式2=a發(fā)生了MatchError錯誤。這里模式匹配不成功,因為 2 = 1 明顯不成了,匹配失敗。前面的例子中,變量進行了重綁定,實際上是進行了重新的模式匹配。

模式匹配本質(zhì)是對等式求值的過程,例如數(shù)學的等式中 x = y + 1。如果我們知道了y的值,自然可以通過數(shù)學表達式運算,求出x的值。模式匹配的本質(zhì)就是針對右邊的表達式或數(shù)字字面量求值,然后根據(jù)等式的原則,計算出左邊的模式的值,再對左邊進行變量的綁定。

模式匹配可以看成表達式運算,那么表達式肯定要是一類東西,而不是別的什么玩意了。例如x = y + 1中的 xy都是變量。如果是這樣的表達式 fn x -> x end = y + 1。左邊是一個匿名函數(shù)式,顯然函數(shù)是不能進行加減運算的。這也就是為什么左邊的變量或表達式稱之為模式

復(fù)雜的例子

下面看幾個復(fù)雜一點的模式匹配。

iex(12)> list = [1, 2, [3, 4, 5]]
[1, 2, [3, 4, 5]]
iex(13)> [a, b, c] = list
[1, 2, [3, 4, 5]]
iex(14)> a
1
iex(15)> b
2
iex(16)> c
[3, 4, 5]
iex(17)>

首先,有一個內(nèi)嵌列表的列表。表達式list=[1, 2, [3, 4, 5]]將列表和變量list進行匹配,這里列表可以看成一個整體,匹配成功,然后進行變量綁定,將變量list綁定成一個列表。

[a, b, c] = list表達式左邊的模式也是一個列表,右邊也是一個列表(同一類型),并且左右兩邊的列表都是三個項目元素(同一結(jié)構(gòu))。匹配成功,然后進行變量綁定。

通過這個例子,可以清楚的知道,模式匹配的模式和表達式或數(shù)值的結(jié)構(gòu)必須要相同。即同一數(shù)據(jù)類型,同一結(jié)構(gòu)類型。

模式可以是數(shù)值和變量,也可以是變量和數(shù)值的混合結(jié)構(gòu)

iex(17)> list = [1, 2, 3]
[1, 2, 3]
iex(18)> [a, 2, c] = list
[1, 2, 3]
iex(19)> a
1
iex(20)> c
3
iex(21)> 2
2
iex(22)>

例子中,右邊的模式中的結(jié)構(gòu)和右邊一致 ,并且第二個元素都是2,匹配成功,剩下的ac也可以匹配成功,進行變量綁定。

變量是可以重新綁定,但是在一次模式匹配中,變量只能綁定一次

iex(22)> [a, a] = [1, 1]
[1, 1]
iex(23)> [a, a] = [1, 2]
** (MatchError) no match of right hand side value: [1, 2]
iex(23)>

第二個表示中,含有兩個a變量的列表在結(jié)構(gòu)和數(shù)據(jù)類型都和左邊相匹配,可是進行變量綁定的時候,a只能綁定一次,因此整個模式匹配過程失敗。

想要重新綁定變量,只需要再進行一次模式匹配即可:

iex(23)> a = 1
1
iex(24)> [1, a] = [1, 2]
[1, 2]
iex(25)> a
2
iex(26)>

固定綁定

函數(shù)式本來追求的是不變,如果也想要變量綁定了之后不被重新綁定。可以使用脫字符^將變量固定。該符號很形象,就像釘子一樣固定了變量。

iex(2)> a = 1
1
iex(3)> a = 2
2
iex(5)> ^a = 3
** (MatchError) no match of right hand side value: 3
iex(5)> a
2
iex(6)>

忽略綁定

變量可以重新綁定,也可以固定綁定,當然還可以忽略綁定。與其他語言例如python,swift類似。Elixir可以使用下劃線_忽略變量的綁定。

iex(6)> [1, a] = [1, 2]
[1, 2]
iex(7)> a
2
iex(8)> [1, _] = [1, 2]
[1, 2]

_就像占位符一樣,雖然它總是能匹配成功,例如可以匹配任何值,可是它并不會綁定任何值。這個功能就是可以在模式匹配中,忽略一些不需要綁定的變量。

總結(jié)

以上就是模式匹配的基礎(chǔ)內(nèi)容。需要重新理解=符號的含義,理解匹配這個過程。匹配的過程類似數(shù)學表達式的求值過程。

整個過程又和純數(shù)學表達式求值有差別,因為模式只能放在左邊,并左右的結(jié)構(gòu)和類型都要一模一樣。右邊如果出現(xiàn)變量和表達式,則會先對右邊的變量或表達式先求值。然后再和左邊的模式進行匹配運算。

如果匹配成功,則對左邊的模式(變量)進行變量綁定。一次模式匹配只能綁定一次變量。不同的模式匹配可以重新綁定變量。可以使用脫字符^不讓變量重新綁定,亦可以使用_忽略綁定。

模式匹配介紹完畢,這個概念存在于很多函數(shù)式編程語言中。剛開始可能很不習慣,覺得和賦值沒有差別。只要重新認識清楚數(shù)學上的等號的含義,就能慢慢的理解。當然實戰(zhàn)才是根本,后面的將會使用到模式匹配,到時心中的疑問將會逐步解開。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容