智能匹配
這兒有一個標準 Perl 6(即在你的編譯單元開始的時候所生效的 Perl 方言) 的智能匹配表格。智能匹配通常作用在當前「主題」(topic)上, 即作用在 $_ 變量上. 在下面的表格中, $_ 代表 ~~ 操作符的左側(cè), 或者代表 given 的參數(shù), 或者代表其它主題化的參數(shù)。 X 代表 ~~ 操作符右側(cè)要與之($_)相匹配的模式, 或者在 when 后面的模式。(并且, 實際上, ~~ 操作符充當著一個小型的主題(topicalizer); 即, 為了右側(cè)的計算, 它把 $_ 綁定到左側(cè)的值上。 使用底層的 .ACCEPTS 形式來避免這種主題化.)
第一節(jié)包含了特殊的(privileged)語法; 如果匹配能通過那些條目之一完成, 那它就會那樣做。 這些特別的語法是通過它們的外形(form)而非它們的類型(type)進行分派的。 否則就使用表格中的剩余部分,并且匹配會根據(jù)正常的方法分派規(guī)則進行分派。 優(yōu)化器(optimizer)被允許假定在編譯時之后沒有定義額外的匹配操作符, 所以, 如果在編譯時模式類型就是顯而易見的話, 那么跳轉(zhuǎn)表(jump table)就可以被優(yōu)化。 然而, 只要 ~~ 操作符是 Perl 中少有的幾個不使用多重分派的操作符之一, 那么這部分表格的語法仍然有些特殊。 相反, 基于類型的智能匹配被直截了當?shù)胤峙山o了屬于 X 模式對象的底層方法.
換句話說, 智能匹配首先根據(jù)模式(pattern)的外形/形式(form)或類型(下面的X)進行分派(dispatch), 然后那個模式自身決定是否關(guān)注和怎樣關(guān)注主題($_)的類型。 所以, 下表中的第二列實際上是初始(primary)列。 第一列中的 Any 條目標示了模式要么不關(guān)心主題的類型, 要么挑選那個條目作為默認項, 因為上面列出的類型越具體,它越不匹配。
$_ X 所隱含的匹配類型 Match if (given $_)
====== ===== ===================== ===================
Any True ~~ True (parsewarn on literal token)
Any False ~~ False match (parsewarn on literal token)
Any Match ~~ Successful match (parsewarn on literal token)
Any Nil ~~ Benign failure (parsewarn on literal token)
Any Failure Failure type check (okay, 與類型相匹配)
Any * block 簽名匹配 block 成功綁定到 |$_
Any Callable:($) item sub truth X($_)
S03-smartmatch/any-callable.t lines 5–14
sub is_even($x) { $x % 2 == 0 }
sub is_odd ($x) { $x % 2 == 1 }
# 這里 Any 代表了數(shù)字 4, X 代表了調(diào)用子例程所返回的布爾值
say 'scalar sub truth (unary)' if 4 ~~ &is_even;
say 'scalar sub truth (unary, negated smart-match)' if 4 !~~ &is_odd;
Any Callable:() simple closure truth X() (ignoring $_)
S03-smartmatch/any-callable.t lines 15–24
Any Bool simple truth X (treats Bool value as success/failure)
S03-smartmatch/any-bool.t lines 5–23
Positional List lists are comparable $_ ?~~? X (but dwims ** wildcards!)
Any Match match success X (treats Match value as success)
Any Nil benign failure X (treats Nil value as failure)
Any Failure malign failure X (passes Failure object through)
Any Numeric 數(shù)值相等 +$_ == X
Any Stringy 字符串相等 ~$_ eq X
Associative Pair test hash mapping $_{X.key} ~~ X.value
Any Pair 測試對象屬性 ?."{X.key}" === ?X.value (例如. 文件測試)
sub is-true() { True };
sub is-false() { False };
say '~~ non-syntactic True' if 0 ~~ is-true();
say '~~ non-syntactic True' if 'a' ~~ is-true();
'a' ~~ .so; # True
0 ~~ .so; # False
0 ~~ .not; # True
# 列表可比較
my @a = "iPad", "iTouch", "iWatch";
say so @a ~~ ("iPad", "iTouch", "iWatch"); # True, 必須是位置和元素都相同才相等
say so @a ~~ ("iPad", "iWatch", "iTouch"); # False
# 判斷數(shù)值相等
my $a = 3;
say so $a ~~ 30; # False
say so $a ~~ 3; # True
# 判斷字符串相等
# my $lan = "Perl 6";
say so $lan ~~ "Perl"; # False
say so $lan ~~ "Perl 6"; # True
S03-smartmatch/any-pair.t lines 5–35
Set Set identical sets $_ === X
Any Setty force set comparison $_.Set === X.Set
Bag Bag identical bags $_ === X
Any Baggy force bag comparison $_.Bag === X.Bag
Mix Mix identical bags $_ === X
Any Mixy force mix comparison $_.Mix === X.Mix
Associative Array keys/list are comparable +X == +$_ and $_{X.all}:exists
Callable Positional list vs predicate so $_(X)
Any Positional lists are comparable $_[] ?===? X[]
Hash Hash hash mapping equivalent $_ eqv X
Associative Hash force hash comparison $_.Hash eqv X
Callable Hash hash vs predicate so $_(X)
Positional Hash attempted any/all FAIL, point user to [].any and [].all for LHS
Pair Hash hash does mapping X{.key} ~~ .value
Any Hash hash contains object X{$_}:exists
Str Regex string pattern match .match(X)
Associative Regex attempted reverse dwim FAIL, point user to any/all vs keys/values/pairs
Positional Regex attempted any/all/cat FAIL, point user to any/all/cat/join for LHS
Any Regex pattern match .match(X)
Range Range subset range !$_ or .bounds.all ~~ X (mod ^'s)
S03-smartmatch/range-range.t lines 5–29
Any Range in real range X.min <= $_ <= X.max (mod ^'s)
S03-smartmatch/disorganized.t lines 30–145
Any Range in stringy range X.min le $_ le X.max (mod ^'s)
Any Range in generic range [!after] X.min,$_,X.max (etc.)
Any Type type membership $_.does(X)
S03-smartmatch/any-type.t lines 5–40
Signature Signature sig compatibility $_ is a subset of X ???
Callable Signature sig compatibility $_.sig is a subset of X ???
Capture Signature parameters bindable $_ could bind to X (doesn't!)
Any Signature parameters bindable |$_ could bind to X (doesn't!)
Signature Capture parameters bindable X could bind to $_
Any Any scalars are identical $_ === X
S03-smartmatch/any-any.t lines 5–29
如果沒有其它模式要求 X, 最后那個 rule 才會被應(yīng)用.
所有的智能匹配類型都是itemized; ~~ 和 given/when 都為它們的參數(shù)提供了 item 上下文, 并自動線程化(autothread)任何 junctive 匹配, 以使最終對 .ACCEPTS 的分派看不到任何「復(fù)數(shù)」(plural). 所以,上面的 $_ 和 X 都是被當作標量的潛在容器對象。 (不過,你可以顯式地使 ~~ 亢奮。 在這種情況下, 所有的智能匹配都是使用 .ACCEPTS 的基于類型的分派來完成的, 不是前面表格中的基于形式的分派)
基于類型的底層方法分派的真正形式是 :
X.ACCEPTS($_)
作為單個分派調(diào)用, 這僅僅只關(guān)注最初的 X 的類型。 ACCEPT 方法接口是通過 Pattern role 來定義的。 任何組成 Pattern role 的類都可以選擇提供單個 ACCEPTS 方法來處理一切, 這對應(yīng)于上面那些左側(cè)只有一個 Any 條目的模式類型?;蛘咴陬愔?類也能選擇提供多個 ACCEPTS multi-methods, 然后這些會在類中根據(jù) $_ 的類型進行重新分派.
智能匹配表格主要用于反應(yīng)編譯時識別的形式和類型. 為了避免條目的激增, 表格假設(shè)下面的類型會有相似的表現(xiàn):
實際類型 Use entries for
=========== ===============
Iterator Seq Array
SetHash BagHash MixHash Hash
named values created with
Class, Enum, or Role,
or generic type binding Type
Char Cat Str
Int UInt etc. Num
Byte Str or Int
Buf Str or Array of Int
(注意, 然而, 這些映射可以顯式地通過定義合適的 ACCEPTS 方法進行重寫。 如果在編譯時, 重定義出現(xiàn)的比智能匹配的分析早, 那么優(yōu)化器也可以可訪問到信息)。
如果并且只有在它跟 Unicode 的關(guān)系被清楚地聲明或類型化時, 一個包含任何ASCII 范圍之外的字節(jié)或整數(shù)的 Buf 類型才可能被靜靜地提升為 Str 類型用于模式匹配。 這種類型信息可能來自輸入文件句柄, 或者 Buf role 可能是一個允許你使用各種已知的編碼來實例化 buffers 的參數(shù)類型。 在沒有這種類型信息的情況下, 你仍然可以跟 buffer 進行模式匹配, 但是任何把 buffer 當作不是整數(shù)序列的嘗試都是錯誤的, 這通常會發(fā)生警告.
與 Grammar 相匹配會把 grammar 當作類型名, 而不是一個 grammar。 你需要使用 .parse 和 .parsefile 方法來調(diào)用一個 grammar.
與 Signature 相匹配不會真的綁定任何變量, 而只是測試那個簽名是否能綁定。 要真的綁定到一個簽名上, 使用模式 * 把綁定代理到 when 語句塊里。 在 when 里面與 * 相匹配很特殊。它從隨后的 block 是否與主題變量相綁定中接收真假, 所以你可以做有序的簽名匹配:
given $capture {
when * -> Int $a, Str $b { ... }
when * -> Str $a, Int $b { ... }
when * -> $a, $b { ... }
when * { ... }
}
當多重分派無序的語義不足以定義代碼的”主從秩序”("pecking order")時, 這會很有用。 注意, 你要么綁定給一個裸的 block 要么綁定給一個箭頭 block(pointy block)。 綁定給裸的 block 很方便的把主題放在 $_ 中,所以上面最后那種形式等價于一個 default.(占位符參數(shù)也能用于裸 block 形式, 盡管它們的類型不能被那樣指定.)
沒有為 Any 模式定義的模式匹配, 所以,如果你想要一個右側(cè)是 Any 的反轉(zhuǎn)的智能匹配測試, 那么你總是通過顯式地調(diào)用使用了 $_ 作為模式的底層 ACCEPTS 方法來獲取它. 例如
$_ X 想要的匹配類型 右側(cè)所使用的東西
====== === ==================== ========================
Callable Any item sub truth .ACCEPTS(X) or .(X)
Range Any in range .ACCEPTS(X)
Type Any type membership .ACCEPTS(X) or .does(X)
Regex Any pattern match .ACCEPTS(X)
etc.
同樣的技巧會允許你傾向于給混合對象以默認匹配規(guī)則, 只要你在 $_ 身上以點方法開頭:
given $somethingordered {
when .values.'[<=]' { say "increasing" }
when .values.'[>=]' { say "decreasing" }
}
必要時, 你可以定義一個宏來得到”反轉(zhuǎn)的 when”:
my macro statement_control:<ACCEPTS> () { "when .ACCEPTS: " }
given $pattern {
ACCEPTS $a { ... }
ACCEPTS $b { ... }
ACCEPTS $c { ... }
}
各種提議但棄用了的智能匹配行為可以很容易地(并且我們希望更易讀)被模仿成下面這樣:
$_ X Type of Match Wanted What to use on the right
====== === ==================== ========================
Array Num array element truth .[X]
Array Num array contains number *,X,*
Array Str array contains string *,X,*
Array Seq array begins with seq X,*
Array Seq array contains seq *,X,*
Array Seq array ends with seq *,X
Hash Str hash element truth .{X}
Hash Str hash key existence .{X}:exists
Hash Num hash element truth .{X}
Hash Num hash key existence .{X}:exists
Buf Int buffer contains int .match(X)
Str Char string contains char .match(X)
Str Str string contains string .match(X)
Array Scalar array contains item .any === X
Str Array array contains string X.any
Num Array array contains number X.any
Scalar Array array contains object X.any
Hash Array hash slice exists .{X.all}:exists .{X.any}:exists
Set Set subset relation .{X.all}:exists
Set Hash subset relation .{X.all}:exists
Any Set subset relation .Set.{X.all}:exists
Any Hash subset relation .Set.{X.all}:exists
Any Set superset relation X.{.all}:exists
Any Hash superset relation X.{.all}:exists
Any Set sets intersect .{X.any}:exists
Set Array subset relation X,* # (conjectured)
Array Regex match array as string .Cat.match(X) cat(@$_).match(X)
(注意, .cat 方法和 Cat 類型強轉(zhuǎn)都接收單個對象, 不像 cat 函數(shù), 它作為一個列表操作符, 接收一個句法的列表(或 multilist ) 并展開它. 然而,所有的這些都返回一個 Cat 對象.)
布爾表達式能返回布爾值, 例如比較操作符或一元 ? 操作符.它們可能顯式或隱式地引用 $_. 如果它們一點也沒有引用 $_, 那也是 okay 的 — 那種情況下你就使用 switch 結(jié)構(gòu)作為比一串 elsifs 可讀性更好的備選分支. 然而, 注意,那意味著你不能這樣寫:
given $boolean {
when True {...}
when False {...}
}
因為它總是會選擇 True 這種情況. 相反, 使用某些在內(nèi)部使用的像條件上下文的東西:
given $boolean {
when .Bool == 1 {...}
when .Bool == 0 {...}
}
最好,就使用 if 語句。在任何情況下, 如果你想使用 ~~ 或 when 進行智能匹配, 它會依照句法識別 True 或 False, 并提醒你它不會按你期望的那樣做. 編譯器也允許在它能檢測的范圍之內(nèi)提醒任何其它不測試 $_ 的布爾結(jié)構(gòu),.
同樣地, 任何接收一個 Matcher的函數(shù)(諸如 grep) 不會接收一個 Bool 類型的參數(shù), 因為那總是標示著編程錯誤.(可以使用 * 來匹配任何東西, 如果那就是你想要的. 或者使用一個返回常量布爾值的閉包)
也要注意 regex 匹配不返回 Bool, 而是返回一個能用作布爾值的 Match 對象(或 Nil). 如果想要, 使用顯式的 ? 或 so 來強制轉(zhuǎn)為布爾值. Match 對象代表一個成功的匹配, 并被智能匹配當作與 True 的值相同, 類似地, Nil 代表著失敗, 并且不能直接用于智能匹配的右側(cè). 測試 definedness 來代替或使用 * === Nil.
(這一段可能有誤)操作符的主要用途是在布爾上下文中返回一個 boolean-ish 的值。然而,對于某些諸如正則表達式的操作數(shù),在 item 或列表上下文中操作符的使用把上下文轉(zhuǎn)換為那個操作數(shù),以至于,例如。,正則表達式可以返回一個所匹配的子字符串的列表,就像 Perl 5中一樣。這可以通過返回一個能在列表上下文中返回一個列表或者在布爾上下文中返回一個布爾值的對象來完成。正則表達式匹配 Match 對象的情況是一種捕獲(Capture), 它擁有這些能力。
帶有諸如 :g那樣的修飾符的正則匹配想返回多個匹配, 使用 List來做 so.就像任何列表一樣, 如果有一個或多項, 值就是被計算為真. 如果沒有匹配, 返回空列表, 它在布爾上下文中計算為 false.
為了智能匹配, 所有的 Set, Bag, 和 Mix 值和對應(yīng)的散列類型, SetHash, BagHash, 和 MixHash 是等價的. 即, Hash 容器中鍵代表唯一對象, 鍵值代表那些唯一鍵的重復(fù)次數(shù).(當然, Set 能有 0 或 1次重復(fù), 因為保證唯一性). 所以,所有這些 Mixy 類型只比較鍵, 不比較鍵值. 使用 eqv 來測試鍵和鍵值的相等性.
盡管為了執(zhí)行智能匹配需要一個檢測范圍邊界的實現(xiàn), 對于任何按順序的類型,例如數(shù)字,字符串,或版本號,兩個 Range 對象智能匹配的結(jié)果實際上沒有按照邊界定義,而是作為在通過設(shè)定的間隔所包圍的兩個值的集合之間的子集關(guān)系。如果并且只有所有能被左側(cè)的范圍所匹配的潛在元素也能被右側(cè)的范圍所匹配結(jié)果才被定義為真。因此一個空范圍被"過度指定"到什么長度是無關(guān)緊要的。如果左側(cè)的范圍為空,那么它總是匹配,因為不存在偽造它的值。如果右側(cè)的范圍為空,那么只有左側(cè)的范圍為空時它才能匹配。
Cat 類型允許你擁有一個無限可擴展的字符串。你可以通過把數(shù)組或迭代器「喂」(feeding)給一個 Cat 來匹配數(shù)組或迭代器, 它本質(zhì)上是某種形式的迭代器上的一個 Str 接口。然后一個 Regex 可以與之相匹配就像它是一個普通的字符串一樣。正則引擎可以詢問那個字符串是否擁有更多字符,并且如果可能的話字符串會從它底層的迭代器中擴展自己。(注意這樣的字符串擁有不確定數(shù)量的字符, 所以如果你在模式中使用了 .*, 或者你詢問那個字符串里面有多少個字符,或者甚至你打印整個字符串,它可能感到有必要吞噬完剩余的字符串,這可能也可能不夠敏捷。)
「cat」 操作符接收一個(潛在惰性的)列表并返回一個 「Cat」 對象。在字符串上下文中, 這惰性地把列表中的每一個元素強轉(zhuǎn)為字符串, 表現(xiàn)為不確定長度的字符串。 你可以像這樣搜索一個 gather:
my $lazystr := cat gather for @foo { take .bar }
$lazystr ~~ /pattern/;
Cat 接口允許 regex 使用 <,> 斷言來匹配元素的邊界, 并且那個匹配所返回的 StrPos 對象可以被分解為在列表元素中的元素索引和位置。 如果底層的數(shù)據(jù)結(jié)構(gòu)是一個可變數(shù)組, 那么對數(shù)組所做的更改(例如通過 shift 或 pop)由 Cat 追蹤, 以使元素編號仍然正確。 字符串, 數(shù)組, 列表, 序列, 捕獲還有樹節(jié)點都可以通過正則表達式或者或多或少的通過可交換的簽名來進行模式匹配。