檢視原始碼 Erlang 中的匹配規範
「匹配規範」(match_spec
)是一個 Erlang 詞彙,用於描述嘗試匹配某項事物的簡短「程式」。它可以用於透過 erlang:trace_pattern/3
控制追蹤,或使用例如 ets:select/2
在 ETS 表格中搜尋物件。匹配規範在許多方面都像 Erlang 中的小型函式,但它是由 Erlang 執行時系統解譯/編譯,效率遠高於呼叫 Erlang 函式。與真正的 Erlang 函式的表達能力相比,匹配規範也受到很大的限制。
匹配規範與 Erlang fun 之間最顯著的差異在於語法。匹配規範是 Erlang 詞彙,而非 Erlang 程式碼。此外,匹配規範對於例外狀況有一個奇怪的概念。
- 在
MatchCondition
部分(類似 Erlang guard)中的例外狀況(例如badarg
)會產生立即失敗。 - 在
MatchBody
部分(類似 Erlang 函式主體)中的例外狀況會被隱式捕獲,並產生單一原子'EXIT'
。
文法
用於追蹤的匹配規範可以用以下非正式文法描述
- MatchExpression ::= [ MatchFunction, ... ]
- MatchFunction ::= { MatchHead, MatchConditions, MatchBody }
MatchHead ::= MatchVariable |
'_'
| [ MatchHeadPart, ... ]MatchHeadPart ::= term() | MatchVariable |
'_'
- MatchVariable ::= '$<number>'
MatchConditions ::= [ MatchCondition, ...] |
[]
MatchCondition ::= { GuardFunction } | { GuardFunction, ConditionExpression, ... }
BoolFunction ::=
is_atom
|is_float
|is_integer
|is_list
|is_number
|is_pid
|is_port
|is_reference
|is_tuple
|is_map
|is_map_key
|is_binary
|is_bitstring
|is_boolean
|is_function
|is_record
|is_seq_trace
|'and'
|'or'
|'not'
|'xor'
|'andalso'
|'orelse'
ConditionExpression ::= ExprMatchVariable | { GuardFunction } | { GuardFunction, ConditionExpression, ... } | TermConstruct
ExprMatchVariable ::= MatchVariable (在 MatchHead 中繫結) |
'$_'
|'$$'
TermConstruct = {{}} | {{ ConditionExpression, ... }} |
[]
| [ConditionExpression, ...] |#{}
| #{term() => ConditionExpression, ...} | NonCompositeTerm | Constant- NonCompositeTerm ::= term() (非列表或元組或映射)
- Constant ::= {
const
, term()} GuardFunction ::= BoolFunction |
abs
|element
|hd
|length
|map_get
|map_size
|max
|min
|node
|float
|round
|floor
|ceil
|size
|bit_size
|byte_size
|tuple_size
|tl
|trunc
|binary_part
|'+'
|'-'
|'*'
|'div'
|'rem'
|'band'
|'bor'
|'bxor'
|'bnot'
|'bsl'
|'bsr'
|'>'
|'>='
|'<'
|'=<'
|'=:='
|'=='
|'=/='
|'/='
|self
|get_tcw
- MatchBody ::= [ ActionTerm ]
ActionTerm ::= ConditionExpression | ActionCall
ActionCall ::= {ActionFunction} | {ActionFunction, ActionTerm, ...}
ActionFunction ::=
set_seq_token
|get_seq_token
|message
|return_trace
|exception_trace
|process_dump
|enable_trace
|disable_trace
|trace
|display
|caller
|caller_line
|current_stacktrace
|set_tcw
|silent
用於 ets
的匹配規範可以用以下非正式文法描述
- MatchExpression ::= [ MatchFunction, ... ]
- MatchFunction ::= { MatchHead, MatchConditions, MatchBody }
MatchHead ::= MatchVariable |
'_'
| { MatchHeadPart, ... }MatchHeadPart ::= term() | MatchVariable |
'_'
- MatchVariable ::= '$<number>'
MatchConditions ::= [ MatchCondition, ...] |
[]
MatchCondition ::= { GuardFunction } | { GuardFunction, ConditionExpression, ... }
BoolFunction ::=
is_atom
|is_float
|is_integer
|is_list
|is_number
|is_pid
|is_port
|is_reference
|is_tuple
|is_map
|is_map_key
|is_binary
|is_bitstring
|is_boolean
|is_function
|is_record
|'and'
|'or'
|'not'
|'xor'
|'andalso'
|'orelse'
ConditionExpression ::= ExprMatchVariable | { GuardFunction } | { GuardFunction, ConditionExpression, ... } | TermConstruct
ExprMatchVariable ::= MatchVariable (在 MatchHead 中繫結) |
'$_'
|'$$'
TermConstruct = {{}} | {{ ConditionExpression, ... }} |
[]
| [ConditionExpression, ...] | #{} | #{term() => ConditionExpression, ...} | NonCompositeTerm | Constant- NonCompositeTerm ::= term() (非列表或元組或映射)
- Constant ::= {
const
, term()} GuardFunction ::= BoolFunction |
abs
|element
|hd
|length
|map_get
|map_size
|max
|min
|node
|float
|round
|floor
|ceil
|size
|bit_size
|byte_size
|tuple_size
|tl
|trunc
|binary_part
|'+'
|'-'
|'*'
|'div'
|'rem'
|'band'
|'bor'
|'bxor'
|'bnot'
|'bsl'
|'bsr'
|'>'
|'>='
|'<'
|'=<'
|'=:='
|'=='
|'=/='
|'/='
|self
- MatchBody ::= [ ConditionExpression, ... ]
函式描述
所有類型的匹配規範中允許的函式
在 match_spec
中允許的函式運作方式如下
is_atom
、is_boolean
、is_float
、is_integer
、is_list
、is_number
、is_pid
、is_port
、is_reference
、is_tuple
、is_map
、is_binary
、is_bitstring
、is_function
- 與 Erlang 中對應的 guard 測試相同,傳回true
或false
。is_record
- 接受一個額外的參數,此參數必須是record_info(size, <record_type>)
的結果,例如{is_record, '$1', rectype, record_info(size, rectype)}
。'not'
- 對其單一引數取反(任何不是false
的值都會傳回false
)。'and'
- 如果其所有引數(可變長度的引數列表)評估為true
,則傳回true
,否則傳回false
。評估順序未定義。'or'
- 如果其任何引數評估為true
,則傳回true
。可變長度的引數列表。評估順序未定義。'andalso'
- 與'and'
的運作方式相同,但當一個引數評估為非true
的值時,會停止評估其引數。引數會從左到右評估。'orelse'
- 與'or'
的運作方式相同,但只要其一個引數評估為true
,就會停止評估。引數會從左到右評估。'xor'
- 只有兩個引數,其中一個必須為true
,另一個必須為false
,才會傳回true
;否則,'xor'
會傳回 false。abs
、element
、hd
、length
、map_get
、map_size
、max
、min
、node
、round
、ceil
、floor
、float
、size
、bit_size
、byte_size
、tuple_size
、tl
、trunc
、binary_part
、'+'
、'-'
、'*'
、'div'
、'rem'
、'band'
、'bor'
、'bxor'
、'bnot'
、'bsl'
、'bsr'
、'>'
、'>='
、'<'
、'=<'
、'=:='
、'=='
、'=/='
、'/='
、self
- 與對應的 Erlang BIF(或運算子)相同。如果引數錯誤,結果取決於上下文。在表示式的MatchConditions
部分中,測試會立即失敗(如 Erlang guard)。在MatchBody
部分中,例外狀況會被隱式捕獲,而呼叫會產生原子'EXIT'
。
僅允許用於追蹤的函式
僅允許用於追蹤的函式運作方式如下
is_seq_trace
- 如果為目前的程序設定了循序追蹤權杖,則傳回true
,否則傳回false
。set_seq_token
- 與seq_trace:set_token/2
的運作方式相同,但成功時傳回true
,錯誤或引數錯誤時傳回'EXIT'
。僅允許在MatchBody
部分中使用,且僅允許在追蹤時使用。get_seq_token
- 與seq_trace:get_token/0
相同,且僅允許在追蹤時的MatchBody
部分使用。message
- 設定附加於傳送的追蹤訊息的額外訊息。在主體中只能設定一個額外訊息。後續的呼叫會取代附加的訊息。在特殊情況下,
{message, false}
會停用此函式呼叫的追蹤訊息傳送('call' 和 'return_to'),如同比對規範未匹配一樣。如果只需要MatchBody
部分的副作用,這會很有用。另一種特殊情況是
{message, true}
,它會設定預設行為,如同該函式沒有比對規範一樣;追蹤訊息會在沒有額外資訊的情況下傳送(如果{message, true}
之前沒有其他對message
的呼叫,它實際上是一個「空操作」)。接受一個參數:訊息。傳回
true
,且僅能於追蹤時在MatchBody
部分使用。return_trace
- 導致在目前函式返回時傳送return_from
追蹤訊息。不接受任何引數,傳回true
,且僅能於追蹤時在MatchBody
部分使用。如果程序追蹤旗標silent
處於啟用狀態,則會抑制return_from
追蹤訊息。警告: 如果追蹤的函式是尾遞迴,此比對規範函式會破壞該屬性。因此,如果對永久伺服器程序使用執行此函式的比對規範,則只能在有限的時間內啟用,否則模擬器最終會使用主機中的所有記憶體並當機。如果使用程序追蹤旗標
silent
抑制此比對規範函式,則尾遞迴仍然保留。exception_trace
- 功能與return_trace
相同,另外,如果追蹤的函式因為例外狀況而退出,則無論例外狀況是否被捕獲,都會產生exception_from
追蹤訊息。process_dump
- 以二進制形式傳回有關目前程序的一些文字資訊。不接受任何引數,且僅允許在追蹤時於MatchBody
部分使用。enable_trace
- 為程序啟用追蹤旗標。帶有一個參數時,此函式會啟用追蹤,如同 Erlang 呼叫
trace:process(S, self(), true, [P2])
,其中S
是目前的追蹤會期,而P2
是enable_trace
的參數。帶有兩個參數時,第一個參數必須是程序識別碼或程序的註冊名稱。在這種情況下,會為指定的程序啟用追蹤,如同 Erlang 呼叫
trace:process(S, P1, true, [P2])
,其中P1
是第一個參數,而P2
是第二個參數。P1
不能是原子all
、new
或existing
中的其中一個(除非它們是註冊名稱)。P2
不能是cpu_timestamp
或tracer
。傳回
true
,且僅能於追蹤時在MatchBody
部分使用。如果由舊版函式
erlang:trace_pattern/3
使用,則程序P1
會將其追蹤訊息傳送到執行此陳述式之程序所使用的相同追蹤器。disable_trace
- 為程序停用追蹤旗標。帶有一個參數時,此函式會停用追蹤,如同 Erlang 呼叫
trace:process(S, self(), false, [P2])
,其中S
是目前的追蹤會期,而P2
是disable_trace
的參數。帶有兩個參數時,此函式的作用如同 Erlang 呼叫
trace:process(S, P1, false, [P2])
,其中P1
可以是程序識別碼或註冊名稱,並指定為比對規範函式的第一個參數。P2
不能是cpu_timestamp
或tracer
。傳回
true
,且僅能於追蹤時在MatchBody
部分使用。trace
- 為程序啟用和/或停用追蹤旗標。帶有兩個參數時,此函式會將要停用的追蹤旗標清單作為第一個參數,並將要啟用的追蹤旗標清單作為第二個參數。邏輯上,會先套用停用清單,但實際上所有變更都會以原子方式套用。追蹤旗標與
trace:process/4
的相同,不包括cpu_timestamp
。當此函式帶有三個參數時,第一個參數是要設定追蹤旗標的程序識別碼或程序的註冊名稱,第二個參數是停用清單,而第三個參數是啟用清單。
當透過新的
trace
API 使用時,不允許使用追蹤旗標tracer
,且接收追蹤器永遠是目前會期的追蹤器。當透過舊版函式
erlang:trace_pattern/3
使用時,允許使用追蹤旗標tracer
。如果未指定追蹤器,則會使用與執行比對規範的程序相同的追蹤器(而非中繼追蹤器)。如果該程序也沒有追蹤器,則會忽略追蹤旗標。當使用 追蹤器模組時,必須在執行比對規範之前載入該模組。如果未載入,則比對會失敗。
如果變更了追蹤目標程序的任何追蹤屬性,則傳回
true
,否則傳回false
。僅能於追蹤時在MatchBody
部分使用。caller
- 以元組{Module, Function, Arity}
的形式傳回呼叫函式,如果無法判斷呼叫函式,則傳回原子undefined
。僅能於追蹤時在MatchBody
部分使用。請注意,如果追蹤的是「技術內建函式」(即不是以 Erlang 編寫的函式),則
caller
函式有時會傳回原子undefined
。在這種呼叫期間,無法使用呼叫的 Erlang 函式。caller_line
- 與caller
類似,但會傳回有關呼叫函式中函式呼叫位置的原始程式碼的其他資訊。以元組{Module, Function, Arity, {File, Line}}
的形式傳回呼叫函式。File
是 字串檔案名稱,而Line
是原始程式碼行號。如果無法判斷File
和Line
,則會傳回{Module, Function, Arity, undefined}
。如果無法判斷呼叫函式,則會傳回原子undefined
。僅能於追蹤時在MatchBody
部分使用。請注意,如果追蹤的是「技術內建函式」(即不是以 Erlang 編寫的函式),則
caller_line
函式有時會傳回原子undefined
。在這種呼叫期間,無法使用呼叫的 Erlang 函式。current_stacktrace
- 傳回呼叫函式的目前呼叫堆疊回溯(堆疊追蹤)。堆疊的格式與try
的catch
部分中的格式相同。請參閱 呼叫堆疊回溯 (stacktrace)。堆疊追蹤的深度會根據backtrace_depth
系統旗標設定而截斷。接受深度參數。如果引數較大,深度值將為
backtrace_depth
。display
- 僅用於偵錯目的。在stdout
上將單一引數顯示為 Erlang 項,這很少是想要的結果。傳回true
,且僅能於追蹤時在MatchBody
部分使用。get_tcw
- 不接受任何引數,並傳回節點的追蹤控制字的值。erlang:system_info(trace_control_word)
會執行相同的操作。追蹤控制字是一個 32 位元無符號整數,旨在用於通用追蹤控制。可以從追蹤比對規範內部和使用 BIF 來測試和設定追蹤控制字。此呼叫僅允許在追蹤時進行。
set_tcw
- 接受一個無符號整數引數,將節點的追蹤控制字的值設定為引數的值,並傳回先前的值。erlang:system_flag(trace_control_word, Value)
會執行相同的操作。僅允許在追蹤時於MatchBody
部分使用set_tcw
。silent
- 接受一個引數。如果引數為true
,則目前程序的呼叫追蹤訊息模式會設定為靜音,以用於此呼叫和所有後續呼叫,也就是說,即使在追蹤函式的MatchBody
部分呼叫{message, true}
,也會抑制呼叫追蹤訊息。此模式也可以透過
erlang:trace/3
的旗標silent
啟用。如果引數為
false
,則目前程序的呼叫追蹤訊息模式會設定為正常(非靜音),以用於此呼叫和所有後續呼叫。如果引數不是
true
或false
,則呼叫追蹤訊息模式不受影響。
注意
所有「函式呼叫」都必須是元組,即使它們不接受任何引數。
self
的值是原子()self
,但{self}
的值是目前程序的 pid()。
比對目標
每個比對規範的執行都會針對比對目標項進行。目標項的格式和內容取決於執行比對的內容。ETS 的比對目標永遠是完整的表格元組。呼叫追蹤的比對目標永遠是所有函式引數的清單。事件追蹤的比對目標取決於事件類型,請參閱下表。
內容 | 類型 | 比對目標 | 說明 |
---|---|---|---|
ETS | {Key, Value1, Value2, ...} | 表格物件 | |
追蹤 | 呼叫 | [Arg1, Arg2, ...] | 函式引數 |
追蹤 | 傳送 | [Receiver, Message] | 接收程序/連接埠和訊息項 |
追蹤 | 'receive' | [Node, Sender, Message] | 傳送節點、處理序/埠和訊息項 |
表格:根據上下文比對目標
變數和字面值
變數的形式為 '$<數字>'
,其中 <數字>
是介於 0 和 100,000,000 (1e+8) 之間的整數。如果數字超出這些限制,則行為是未定義的。在 MatchHead
部分,特殊變數 '_'
會比對任何值,並且永遠不會被綁定(就像 Erlang 中的 _
一樣)。
- 在
MatchCondition/MatchBody
部分中,不允許未綁定的變數,因此'_'
會被解釋為它自己(一個原子)。變數只能在MatchHead
部分中被綁定。 - 在
MatchBody
和MatchCondition
部分中,只能使用先前綁定的變數。 - 作為一種特殊情況,以下適用於
MatchCondition/MatchBody
部分- 變數
'$_'
會展開為整個比對目標項。 - 變數
'$$'
會展開為依序排列的所有已綁定變數的值列表(也就是['$1','$2', ...]
)。
- 變數
在 MatchHead
部分,所有字面值(上述變數除外)都會被「照原樣」解釋。
在 MatchCondition/MatchBody
部分,解釋方式在某些方面有所不同。這些部分中的字面值可以「照原樣」寫入,這適用於所有字面值,除了元組之外,或者使用特殊形式 {const, T}
,其中 T
是任何 Erlang 項。
對於比對規格中的元組字面值,也可以使用雙元組括號,也就是將它們構造成包含單一元組的一元組,而該單一元組是要被構建的元組。「雙元組括號」語法可用於從已綁定的變數建構元組,例如 {{'$1', [a,b,'$2']}}
。範例
表達式 | 變數綁定 | 結果 |
---|---|---|
{{'$1','$2'}} | '$1' = a, '$2' = b | {a,b} |
{const, {'$1', '$2'}} | 不相關 | {'$1', '$2'} |
a | 不相關 | a |
'$1' | '$1' = [] | [] |
[{{a}}] | 不相關 | [{a}] |
['$1'] | '$1' = [] | [[]] |
42 | 不相關 | 42 |
"hello" | 不相關 | "hello" |
$1 | 不相關 | 49 (字元 '1' 的 ASCII 值) |
表格:比對規格的 MatchCondition/MatchBody 部分中的字面值
比對的執行
當執行階段系統決定是否要傳送追蹤訊息時,比對表達式的執行方式如下
對於 MatchExpression
列表中的每個元組,並且在沒有比對成功的情況下
- 將
MatchHead
部分與比對目標項進行比對,綁定'$<數字>'
變數(很像ets:match/2
中的方式)。如果MatchHead
部分無法比對引數,則比對失敗。 - 評估每個
MatchCondition
(其中只能出現先前在MatchHead
部分中綁定的'$<數字>'
變數),並期望它傳回原子true
。當條件的評估結果不是true
時,比對失敗。如果任何 BIF 呼叫產生例外,比對也會失敗。 - 可能會發生兩種情況
如果比對規格在追蹤時執行
以與
MatchConditions
相同的方式評估每個ActionTerm
,但忽略傳回值。無論這部分發生什麼事,比對都會成功。如果比對規格在從 ETS 表格中選取物件時執行
依序評估表達式,並傳回最後一個表達式的值(通常在此上下文中只有一個表達式)。
ETS 和追蹤中比對規格的差異
ETS 比對規格會產生傳回值。通常,MatchBody
包含一個單一的 ConditionExpression
,其定義傳回值,且不產生任何副作用。在 ETS 上下文中不允許有副作用的呼叫。
追蹤時沒有要產生的傳回值,比對規格會比對或不比對。當表達式比對成功時,其效果是追蹤訊息,而不是傳回的項。ActionTerm
的執行方式與命令式語言相同,也就是為了其副作用而執行。追蹤時也允許有副作用的函數。
追蹤範例
比對三個引數的引數列表,其中第一個和第三個引數相等
[{['$1', '_', '$1'],
[],
[]}]
比對三個引數的引數列表,其中第二個引數是大於 3 的數字
[{['_', '$1', '_'],
[{ '>', '$1', 3}],
[]}]
比對三個引數的引數列表,其中第三個引數是包含引數一和二的元組,或者是以引數一和二開頭的列表(也就是 [a,b,[a,b,c]]
或 [a,b,{a,b}]
)
[{['$1', '$2', '$3'],
[{'orelse',
{'=:=', '$3', {{'$1','$2'}}},
{'and',
{'=:=', '$1', {hd, '$3'}},
{'=:=', '$2', {hd, {tl, '$3'}}}}}],
[]}]
上述問題也可以如下解決
[{['$1', '$2', {'$1', '$2}], [], []},
{['$1', '$2', ['$1', '$2' | '_']], [], []}]
比對兩個引數,其中第一個引數是以一個列表開頭的元組,而該列表又以第二個引數的兩倍值開頭(也就是 [{[4,x],y},2]
或 [{[8], y, z},4])
[{['$1', '$2'],[{'=:=', {'*', 2, '$2'}, {hd, {element, 1, '$1'}}}],
[]}]
比對三個引數。當所有三個引數都相等且為數字時,將處理序傾印附加到追蹤訊息,否則讓追蹤訊息「照原樣」,但將循序追蹤權杖標籤設定為 4711
[{['$1', '$1', '$1'],
[{is_number, '$1'}],
[{message, {process_dump}}]},
{'_', [], [{set_seq_token, label, 4711}]}]
如上所述,可以將引數列表與單一 MatchVariable
或 '_'
比對。將整個引數列表替換為單一變數是一種特殊情況。在所有其他情況下,MatchHead
必須是適當的列表。
僅當追蹤控制字設定為 1 時,才產生追蹤訊息
[{'_',
[{'==',{get_tcw},{const, 1}}],
[]}]
僅當有 seq_trace
權杖時,才產生追蹤訊息
[{'_',
[{'==',{is_seq_trace},{const, 1}}],
[]}]
當第一個引數是 'verbose'
時,移除 'silent'
追蹤旗標;當它是 'silent'
時,則加上它:
[{'$1',
[{'==',{hd, '$1'},verbose}],
[{trace, [silent],[]}]},
{'$1',
[{'==',{hd, '$1'},silent}],
[{trace, [],[silent]}]}]
如果函數的arity 為 3,則加入 return_trace
訊息
[{'$1',
[{'==',{length, '$1'},3}],
[{return_trace}]},
{'_',[],[]}]
僅當函數的 arity 為 3 且第一個引數為 'trace'
時,才產生追蹤訊息
[{['trace','$2','$3'],
[],
[]},
{'_',[],[]}]
ETS 範例
比對 ETS 表格中的所有物件,其中第一個元素是原子 'strider'
且元組的 arity 為 3,並傳回整個物件
[{{strider,'_','_'},
[],
['$_']}]
比對 ETS 表格中 arity > 1 且第一個元素為 'gandalf' 的所有物件,並傳回元素 2
[{'$1',
[{'==', gandalf, {element, 1, '$1'}},{'>=',{size, '$1'},2}],
[{element,2,'$1'}]}]
在此範例中,如果第一個元素是索引鍵,則在 MatchHead
部分中比對該索引鍵,比在 MatchConditions
部分中比對效率更高。表格的搜尋空間會受到 MatchHead
的限制,因此只會搜尋具有相符索引鍵的物件。
比對三個元素的元組,其中第二個元素是 'merry'
或 'pippin'
,並傳回整個物件
[{{'_',merry,'_'},
[],
['$_']},
{{'_',pippin,'_'},
[],
['$_']}]
函數 ets:test_ms/2
對於測試複雜的 ETS 比對非常有用。