Perl 6 中的智能匹配

智能匹配

這兒有一個標準 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é)點都可以通過正則表達式或者或多或少的通過可交換的簽名來進行模式匹配。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 智能匹配 智能匹配通常作用在當前”主題”(topic)上, 即作用在 $_ 變量上. 在下面的表格中, $_ 代表...
    焉知非魚閱讀 599評論 0 0
  • 允許的修飾符 有些修飾符能在所有允許的地方出現(xiàn), 但并非所有的都這樣. 通常, 影響 regex 編譯的修飾符(...
    焉知非魚閱讀 1,589評論 0 1
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,553評論 19 139
  • 從匹配中返回值 Match 對象 成功的匹配總是返回一個 Match 對象, 這個對象通常也被放進 $/ 中, (...
    焉知非魚閱讀 1,937評論 0 1
  • 操作符 操作符優(yōu)先級 在像 1 + 2 * 3 這樣的表達式中, 2 * 3 被首先計算, 因為中綴操作符 * ...
    焉知非魚閱讀 1,593評論 0 1

友情鏈接更多精彩內(nèi)容