- infix:<...>, 序列操作符.
作為一個(gè)中綴操作符, ... 操作符的左右兩側(cè)都有一個(gè)列表,并且為了產(chǎn)生想要的值的序列,序列操作符 ... 會(huì)盡可能地對(duì)序列進(jìn)行惰性求值。列表被展平后求值。就像所有的中綴操作符一樣, ... 序列操作符比逗號(hào)的優(yōu)先級(jí)要低,所以你沒(méi)必要在逗號(hào)列表的兩側(cè)加上圓括號(hào)。
序列操作符 ... 以右側(cè)列表的第一個(gè)值開(kāi)始。這只在右側(cè)的列表中, ... 序列操作符唯一感興趣的值;any additional list elements are treasured up lazily to be returned after the ... is done.
... 右側(cè)的第一個(gè)值是序列的端點(diǎn)或界限,這是序列操作符 ... 從左側(cè)生成的。
一旦我們知道了序列的邊界,左側(cè)的列表會(huì)一項(xiàng)一項(xiàng)地被求值,并且普通數(shù)字或字符串值被無(wú)差異地傳遞(在右側(cè)邊界允許的程度內(nèi))如果序列中的任何值匹配到邊界值,序列會(huì)終止,包括那個(gè)最后的邊界值在內(nèi)。要排除邊界值,使用 ...^ 代替。
在內(nèi)部,這兩種形式用于檢測(cè)匿名的循環(huán)是否會(huì)終止,而循環(huán)返回序列中的值。假設(shè)下一個(gè)候選的值存儲(chǔ)在 $x 中,并且右側(cè)序列中的第一個(gè)值存儲(chǔ)在 $limit 中,這兩個(gè)操作符各自實(shí)現(xiàn)為:
... last($x) if $x ~~ $limit;
...^ last if $x ~~ $limit;
如果邊界是 * ,序列就沒(méi)有界限。如果邊界是一個(gè)閉包,它會(huì)在當(dāng)前候選對(duì)象中進(jìn)行布爾真值求值,并且序列會(huì)一直繼續(xù)只要閉包返回false。如果邊界是一個(gè)含有一個(gè)或無(wú)限參數(shù)的閉包,
my $lim = 0;
1,2,3 ...^ * > $lim # returns (), since 1 > 0
這個(gè)操作符如果只能把左邊的值原樣返回就太乏味了。它的強(qiáng)大來(lái)自于能從舊值生成新值。你可以,例如,使用一個(gè)存在的生成器產(chǎn)生一個(gè)無(wú)窮列表:
1..* ... * >= $lim
@fib ... * >= $lim
例如:
> 1..* ... * >= 10 # 1 2 3 4 5 6 7 8 9 10
更一般地,如果 ... 操作符左側(cè)列表中的下一項(xiàng)是一個(gè)閉包,它不會(huì)被返回。他會(huì)在已經(jīng)存在的列表的末尾被調(diào)用以產(chǎn)生一個(gè)新值。閉包中變量的數(shù)目決定了要使用多少前置值作為輸入來(lái)生成序列中的下一個(gè)值。例如,以2為步長(zhǎng)計(jì)數(shù)只需要一個(gè)參數(shù):
2, { $^a + 2 } ... * # 2,4,6,8,10,12,14,16...
生成裴波納契序列一次需要兩個(gè)參數(shù):
1, 1, { $^a + $^b } ... * # 1,1,2,3,5,8,13,21...
任何特定的函數(shù)也有效,只要你把它作為列表中的一個(gè)值而不是調(diào)用它:
1, 1, &infix:<+> ... * # 1,1,2,3,5,8...
1, 1, &[+] ... * # 同上
1, 1, *+* ... * # 同上
> sub infix:<jia>($a,$b){ return $a+$b }
> 1 jia 5 # 6
> 1,1,&[jia] ... * # 1 1 2 3 5 8 13 21 34 55 89 144 233 377 ......
更一般地,函數(shù)是一元的,這種情況下左側(cè)列表中任何額外的值會(huì)被構(gòu)建為人類可讀的文檔:
0,2,4, { $_ + 2 } ... 42 # 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42
使用閉包:
> 0,2,4,-> $init {$init+2} ... 42 # 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42
0,2,4, *+2 ... 42 # same thing
<a b c>, { .succ } ... * # same as 'a'..*
函數(shù)也可以不是單調(diào)函數(shù):
1, -* ... * # 1, -1, 1, -1, 1, -1...
False, &prefix:<!> ... * # False, True, False...
函數(shù)也可以是 0-ary 的, 這個(gè)時(shí)候讓閉包成為第一個(gè)元素是 okay 的:
{ rand } ... * # 一組隨機(jī)數(shù)
函數(shù)還可以是吞噬的(n-ary),在這種情況下,所有前面的值都被傳遞進(jìn)來(lái)(這意味著它們必須都被操作符緩存下來(lái),所以性能會(huì)經(jīng)受考驗(yàn), 你可能會(huì)發(fā)現(xiàn)自己空間泄露了)
函數(shù)的數(shù)量不必與返回值的數(shù)量匹配, 但是如果確實(shí)匹配, 你可能會(huì)插入不相關(guān)的序列:
1,1,{ $^a + 1, $^b * 2 } ... * # 1,1,2,2,3,4,4,8,5,16,6,32...
1,1, ->$a,$b {$a+1,$b*2} ... * # 同上
注意在這種情況下任何界限測(cè)試被應(yīng)用到從函數(shù)返回的整個(gè) parcel 上, 這個(gè) parcel 包含兩個(gè)值。
除了那些函數(shù)簽名所隱含的約束,序列操作符從一個(gè)沒(méi)有對(duì)序列作類型約束的顯式函數(shù)中生成。如果函數(shù)的簽名和已存在的值不匹配,那么序列終止。
S03-sequence/misc.t lines 5–12
如果沒(méi)有提供生成閉包, 并且序列是數(shù)字的,而且序列明顯還是等差的或等比的(通過(guò)檢查它的最后 3 個(gè)值),那么序列操作符就會(huì)推斷出合適的函數(shù):
1, 3, 5 ... * # 奇數(shù)
1, 2, 4 ... * # 2的冪
10,9, 8 ... 0 # 倒數(shù)
即, 假設(shè)我們調(diào)用了最后 3 個(gè)數(shù)字 $a, $b, 和 $c,然后定義:
$ab = $b - $a;
$bc = $c - $b;
如果 $ab == $bc 并且 $ab 不等于0, 那么我們通過(guò)函數(shù) *+$ab 可以推斷出等差數(shù)列。如果 $ab 為 0,并且那3個(gè)值看起來(lái)像數(shù)字,那么函數(shù)推斷為 *+0。
如果它們看起來(lái)不像數(shù)字,那么根據(jù) $b cmp $c 的結(jié)果是 Increasing 還是 Decreasing 來(lái)決定選擇的函數(shù)是 *.succ 還是 *.pred。
如果 cmp 返回 Same 那么會(huì)假定函數(shù)是同一個(gè)。
如果 $ab != $bc 并且 none($a,$b,$c) == 0, 那么會(huì)使用除法而非減法做類似的計(jì)算來(lái)決定是否需要一個(gè)等比數(shù)列。定義:
$ab = $b / $a;
$bc = $c / $b;
如果兩者的商相等(并且是有限的),那么會(huì)推斷出等比函數(shù) {$_ * $bc}。
如果目前為止只有 2 個(gè)值, $a 和 $b, 并且差值 $ab 不為 0, 那么我們就假定函數(shù)為 *+$ab 的等差數(shù)列。
如果 $ab 是 0, 那么再次, 我們使用 *+0 還是 *.succ/*.pred 取決于那兩個(gè)值看起來(lái)是不是數(shù)字。
如果只有一個(gè)值,我們總是通過(guò) *.succ 假定增量(這可能被強(qiáng)制為 .pred 通過(guò)檢查界限,就像下面指定的那樣)因此這些結(jié)果都是一樣的:
1 .. *
1 ... *
1,2 ... *
1,2,3 ... *
<1 2 3> ... *
同樣地, 如果給定的值(s)不是數(shù)字, 就假定為 .succ, 所以這些結(jié)果是一樣的:
'a' .. *
'a' ... *
'a','b' ... *
'a','b','c' ... *
<a b c> ... *
如果序列操作符的左側(cè)是 (), 那我們使用函數(shù) {()} 來(lái)生成一個(gè)無(wú)限的空序列。
如果給定了界限,那這個(gè)界限就必須被精確匹配。如果不能精確匹配,會(huì)產(chǎn)生無(wú)限列表。例如,因?yàn)?趨近"和“相等”不一樣, 下面的兩個(gè)序列都是無(wú)限列表,就像你把界限指定為了 * 而不是 0:
1,1/2,1/4 ... 0 # like 1,1/2,1/4 ... *
1,-1/2,1/4 ... 0 # like 1,-1/2,1/4 ... *
同樣地,這是所有的偶數(shù):
my $end = 7;
0,2,4 ... $end
為了捕捉這樣一種情況, 建議寫(xiě)一個(gè)不等式代替:
0,2,4 ...^ { $_ > $end }
當(dāng)使用了顯式的界限函數(shù),他可能通過(guò)返回任意真值來(lái)終止它的列表。因?yàn)樾蛄胁僮鞣橇斜斫Y(jié)合性的,內(nèi)部函數(shù)后面可以跟著 ... ,然后另外一個(gè)函數(shù)來(lái)繼續(xù)列表,等等。因此:
S03-sequence/misc.t lines 34–100
1, *+1 ... { $_ == 9 },
10, *+10 ... { $_ == 90 },
100, *+100 ... { $_ == 900 }
產(chǎn)生:
1,2,3,4,5,6,7,8,9,
10,20,30,40,50,60,70,80,90,
100,200,300,400,500,600,700,800,900
考慮到?jīng)]有閉包的普通匹配規(guī)則,我們可以把上面的序列更簡(jiǎn)單地寫(xiě)為:
1, 2, 3 ... 9,
10, 20, 30 ... 90,
100, 200, 300 ... 900
甚至僅僅是:
1, 2, 3 ...
10, 20, 30 ...
100, 200, 300 ... 900
因?yàn)橐粋€(gè)精確的匹配界限會(huì)被作為序列的一部分返回,所以提供的那個(gè)精確值是一個(gè)合適類型的值, 而非一個(gè)閉包。
對(duì)于左側(cè)只有一個(gè)值的函數(shù)推斷,最后的值被用于決定是 *.succ 還是 *.pred 更合適。 使用 cmp 來(lái)比較那兩個(gè)值來(lái)決定前進(jìn)的方向。
因此, 序列操作符能自動(dòng)反轉(zhuǎn), 而范圍操作符不會(huì)自動(dòng)反轉(zhuǎn).
'z' .. 'a' # 表示一個(gè)空的范圍
'z' ... 'a' # z y x ... a
你可以使用 ^... 形式來(lái)排除第一個(gè)值:
'z' ^... 'a' # y x ... a
5 ^... 1 # 4, 3, 2, 1
但是你要意識(shí)到, 如果列表的左側(cè)很復(fù)雜, 特別是左側(cè)是另一個(gè)序列時(shí), 肯定會(huì)讓你的讀者困惑:
1, 2, 3 ^... *; # 2, 3 ... !
1, 2, 3 ... 10, 20, 30 ^... *; # 2, 3 ... !?!?
是的, 對(duì)于極端喜歡對(duì)稱性的那些人來(lái)說(shuō), 還有另外一種形式: ^...^
就像數(shù)字?jǐn)?shù)值一樣, 字符串匹配必須是精確的, 否則會(huì)產(chǎn)生無(wú)限序列.
注意下面這個(gè)序列:
1.0, *+0.2 ... 2.0
是使用 Rat 算術(shù)計(jì)算的, 而不是 Num, 所以 2.0 精確地匹配了并結(jié)束了序列.
注意:只有在期望為一個(gè) term 的地方,... 才會(huì)被識(shí)別為 yada 操作符。序列操作符只用于期望中綴操作符出現(xiàn)的地方。
如果你在 ... 前面放置了一個(gè)逗號(hào),那么 ...會(huì)被識(shí)別為 yada 列表操作符 - 表達(dá)當(dāng)列表到達(dá)那點(diǎn)時(shí)會(huì)失敗的要求。
1..20, ... "I only know up to 20 so far mister"
> 1..20, fail "I only know up to 20 so far mister" # I only know up to 20 so far mister
序列的末端是一個(gè)代表單個(gè)代碼點(diǎn)的字符串時(shí),會(huì)拋出一個(gè)特殊的異常, 因?yàn)榈湫偷?,用戶?huì)把這樣一個(gè)字符串看作是字符而非字符串。如果你像這樣說(shuō):
S03-sequence/nonnumeric.t lines 36–101
'A' ... 'z'
"\xff" ... "\0"
> 'A' ... 'z' # A B C D ... Y Z [ \ ] ^ _ ` a b c ... y z
它會(huì)假定你對(duì)按字母順序范圍不感興趣, 所以,代替對(duì)于字符串使用普通的 .succ/.pred, 它會(huì)像這樣使用單調(diào)函數(shù)來(lái)增加或減少底層的代碼點(diǎn)數(shù)字:
'A', { $^prev.ord.succ.chr } ... 'z';
"\xff", { $^prev.ord.pred.chr } ... "\0";
... 操作符是 "yada, yada, yada" 列表操作符, 它在其它東西之間用作函數(shù)原型中得主體。如果它曾經(jīng)被執(zhí)行過(guò)的話會(huì)強(qiáng)烈地抱怨(通過(guò)調(diào)用 fail)。變體 ??? 調(diào)用 warn, 而 !!! 調(diào)用 die。參數(shù)是可選的,但是如果提供了參數(shù)的話,會(huì)被傳遞給 fail、warn 或 die 。 否則系統(tǒng)會(huì)基于上下文為你編造信息,以標(biāo)示你嘗試執(zhí)行了某些已經(jīng)創(chuàng)建過(guò)的東西。
Reduce 操作符
[+] [*] [<] [\+] [\*] 等等.
Sigils as coercions to roles
Sigil Alpha variant
----- -------------
$ Scalar
@ Positional (or Iterable?)
% Associative
& Callable
$(1,2 Z 3,4) # Scalar((1,3),(2,4))
@(1,2 Z 3,4) # ((1,3),(2,4))
%(1,2 Z 3,4) # PairSeq(1 => 3, 2 => 4)
$(1,2 X 3,4) # Scalar((1,3),(1,4),(2,3),(2,4))
@(1,2 X 3,4) # ((1,3),(1,4),(2,3),(2,4))
-> 變成了 . , 就像世界其它地方使用的一樣。有一個(gè)產(chǎn)生編譯時(shí)錯(cuò)誤的偽后綴 postfix:['->'] 操作符以提醒 Perl 5 用戶使用點(diǎn)代替。("pointy block" 在 Perl 6 中使用 -> 要求它前面有空格,當(dāng)箭頭可能會(huì)和后綴混淆時(shí),即,當(dāng)期望一個(gè)中綴時(shí)。在 item 位置上不要求有前置空格。)
字符串連接由 . 變成 ~。字符串追加同樣變成 ~=。
文件測(cè)試操作符不見(jiàn)了。我們現(xiàn)在使用 Pair 作為調(diào)用對(duì)象的方法的模式;
if $filename.IO ~~ :e { say "exists" }
等價(jià)于
if so $filename.IO.e { say "exists" }
Likewise
if $filename.IO ~~ :!e { say "doesn't exist" }
is the same as
if not $filename.IO.e { say "doesn't exist" }
如果你不關(guān)心右側(cè)某個(gè)位置上是什么元素,你可以把這個(gè)元素賦值給 * 記號(hào)。 最后的星號(hào) * 丟棄掉列表的剩余部分:
($a, *, $c) = 1, 2, 3; # 丟棄數(shù)字 2
($a, $b, $c, *) = 1..42; # 丟棄 4..42
(在簽字語(yǔ)法中,一個(gè)裸的 $ 也能忽略單個(gè)參數(shù),而一個(gè)裸的 *@ 可以忽略剩下的參數(shù)。)
列表賦值依次把右側(cè)的列表提供給左側(cè)的每個(gè)容器,而每個(gè)容器可以從右側(cè)列表的前頭接收一個(gè)或多個(gè)元素。如果左側(cè)的元素有任何剩余,警告就會(huì)出現(xiàn),除非左側(cè)的列表以 * 字符結(jié)尾或者右側(cè)的最后的迭代器是按照 * 定義的。因此下面沒(méi)有一個(gè)會(huì)發(fā)出警告:
($a, $b, $c, *) = 1..9999999;
($a, $b, $c) = 1..*;
($a, $b, $c) = 1 xx *;
($a, $b, $c) = 1, 2, *;
然而,這個(gè)會(huì)警告你信息缺失:
($a, $b, $c) = 1, 2, 3, 4;
把列表賦值給標(biāo)量, 你可以這樣寫(xiě):
$a = 1, 2, 3;
($a = 1), 2, 3; # 2 和 3 的上下文為空
顯式的禁用或破壞 item 賦值解釋:
$a = [1, 2, 3]; # 強(qiáng)制構(gòu)建 (或許是最佳實(shí)踐)
$a = (1, 2, 3); # force grouping as syntactic item
$a = list 1, 2, 3; # force grouping using listop precedence
$a = @(1, 2, 3); # same thing
@$a = 1, 2, 3; # 強(qiáng)制列表賦值
$a[] = 1, 2, 3; # same thing
如果一個(gè)函數(shù)是上下文不敏感的并且你想返回一個(gè)標(biāo)量值,如果你想為了下標(biāo)或右邊強(qiáng)制為 item 上下文,那么你必須使用 item(或 $ 或 + 或 ~)。
@a[foo()] = bar(); # foo() 和 bar() 在列表上下文中調(diào)用
@a[item foo()] = item bar(); # foo() 和 bar() 在標(biāo)量上下文中調(diào)用
@a[$(foo())] = $(bar()); # 同樣的東西
@a[+foo()] = +bar(); # foo() 和 bar() 在數(shù)值上下文中調(diào)用
%a{~foo()} = ~bar(); # foo() 和 bar() 在字符串上下文中調(diào)用
Junctive 操作符
S03-junctions/misc.t lines 23–151
S03-junctions/misc.t lines 152–157
S03-junctions/associative.t lines 16–37
S03-junctions/boolean-context.t lines 5–150
|, &, 和 ^ 不再是位操作符了(查看 "Changes to Perl 5 operators"),而是擔(dān)任更高的職務(wù): 它們現(xiàn)在是 junction 構(gòu)造器了。
junction 是等價(jià)于多個(gè)值的單個(gè)值。它們進(jìn)行線程化操作符, 返回另一個(gè)代表結(jié)果的 junction:
S03-operators/misc.t lines 68–89
S03-junctions/misc.t lines 158–168
S03-junctions/misc.t lines 233–499
(1|2|3) + 4; # 5|6|7
(1|2) + (3&4); # (4|5) & (5|6)
就像最后那個(gè)例子中解釋的那樣,當(dāng)單個(gè)操作符應(yīng)用到兩個(gè) junctions 中時(shí),結(jié)果是一個(gè)代表左右可能值得組合的 junction。
Junctions 還有 any, all,one, none 的函數(shù)變體。
這為像這樣的構(gòu)造打開(kāi)了大門。
S03-junctions/misc.t lines 169–191
if $roll == none(1..6) { print "Invalid roll" }
if $roll == 1|2|3 { print "Low roll" }
Junctions 在下標(biāo)中的表現(xiàn):
S03-junctions/misc.t lines 192–207
doit() if @foo[any(1,2,3)]
Junctions 也是沒(méi)有順序的。 所以如果你這樣寫(xiě):
S03-junctions/misc.t lines 208–232
foo() | bar() | baz() == 42
這向編譯器表明 junctional 參數(shù)之間沒(méi)有連接(coupling)。它們可以以任意順序求值或并行地求值。它們也可以是短路的, 只要它們中的任何一個(gè)返回 42, 并且其它的不會(huì)被運(yùn)行?;蛘? 如果并行地運(yùn)行, 第一個(gè)成功的線程會(huì)突然終止其它的線程。一般地, 在 junctions 中, 你可能需要避免帶有副作用的代碼。
把否定操作符和 junctions 用在一起可能會(huì)有問(wèn)題如果原生地自動(dòng)線程化的話。然而,就否定元操作符而言,通過(guò)定義 != 和 ne,我們自動(dòng)地得到了說(shuō)英語(yǔ)的人所想要的 "not raising"。即:
S03-junctions/autothreading.t lines 290–367
if $a != 1 | 2 | 3 {...}
實(shí)際上意味著:
if $a ![==] 1 | 2 | 3 {...}
其中元操作符被重寫(xiě)為某些像高階函數(shù)這樣的東西:
negate((* == *), $a, (1|2|3));
它最終等價(jià)于:
if not $a == 1 | 2 | 3 {...}
它是說(shuō)英語(yǔ)的人所期望的語(yǔ)義,你自己寫(xiě)出后面那種風(fēng)格的形式可能會(huì)更好。
作用在數(shù)組、列表和集合中得 Junctive 方法就像對(duì)應(yīng)的列表操作符那樣工作。然而,作用在散列身上的 junctive 方法,只會(huì)對(duì)散列的鍵進(jìn)行連結(jié)(junction)。使用 listop 形式(或者一個(gè)顯式的 .pairs) 來(lái)連結(jié)(junction) pairs。
各種用于集合和 bags (交集、并集等)的操作符也擁有 junctive 優(yōu)先級(jí)(除了那些返回布爾值得之外,它們實(shí)際上被歸類為鏈?zhǔn)讲僮鞣?/p>