操作符優先級
S03-operators/arith.t lines 46–342
S03-operators/precedence.t lines 5–200
Perl 6 擁有和 Perl 5 同等數量的優先級級別,但是它們散布在不同的地方。這兒,我們列出了從最緊湊到最松散的級別,每一級別還有幾個例子:
最高優先級到最低優先級:
A Level Examples
= ===== ========
N Terms 42 3.14 "eek" qq["foo"] $x :!verbose @$array
L Method postfix .meth .+ .? .* .() .[] .{} .<> .?? .:: .= .^ .:
N Autoincrement ++ --
R Exponentiation **
L Symbolic unary ! + - ~ ? | || +^ ~^ ?^ ^
L Multiplicative * / % %% +& +< +> ~& ~< ~> ?& div mod gcd lcm
L Additive + - +| +^ ~| ~^ ?| ?^
L Replication x xx
X Concatenation ~
X Junctive and & (&) ∩
X Junctive or | ^ (|) (^) ∪ (-)
L Named unary temp let
N Structural infix but does <=> leg cmp .. ..^ ^.. ^..^
C Chaining infix != == < <= > >= eq ne lt le gt ge ~~ === eqv !eqv (<) (elem)
X Tight and &&
X Tight or || ^^ // min max
R Conditional ?? !! ff fff
R Item assignment = => += -= **= xx= .=
L Loose unary so not
X Comma operator , :
X List infix Z minmax X X~ X* Xeqv ...
R List prefix print push say die map substr ... [+] [*] any Z=
X Loose and and andthen
X Loose or or xor orelse
X Sequencer <== ==> <<== ==>>
N Terminator ; {...} unless extra ) ] }
下面使用的兩個 !
符號通常表示任意一對兒擁有相同優先級的操作符, 上表指定的二元操作符的結合性解釋如下(其中 A 代表結合性, associativities ):
結合性 Meaning of $a ! $b ! $c
===== =========================
L left ($a ! $b) ! $c
R right $a ! ($b ! $c)
N non ILLEGAL
C chain ($a ! $b) and ($b ! $c)
X list infix:<!>($a; $b; $c)
對于一元操作符, 這解釋為:
結合性 Meaning of !$a!
===== =========================
L left (!$a)!
R right !($a!)
N non ILLEGAL
(在標準 Perl 中沒有能利用結合性的一元操作符,因為在每一優先級級別中, 標準操作符要么一貫地是前綴,要么是后綴。)
注意列表結合性(X)只在同一操作符之間有效。如果兩個擁有不同列表結合性的操作符擁有相同的優先級,它們彼此間就會被認為是非結合性的,必須使用圓括號來消除歧義。
S03-operators/precedence.t lines 211–245
例如, X
交叉操作符和 Z
拉鏈操作符 都有 "list infix" 優先級,但是:
@a X @b Z @c
是非法的,它必須寫成下面的任意一種:
(@a X @b) Z @c
@a X (@b Z @c)
如果僅有的列表結合性操作符的實現是二進制的, 那么它會被當作是右結合性的。
標準的優先級層級嘗試和它們的結合性相一致, 但是用戶定義的操作符和優先級級別可以在同一優先級級別上混合右結合性和左結合性操作符。如果在同一個表達式中不小心使用了有沖突的操作符, 那么操作符彼此之間會被認為是非結合性的, 并且必須使用圓括號來消除歧義。
如果你沒有在上面看見你喜歡的操作符, 下面的章節會包含所有按優先級排列的操作符。這兒描述了基本的操作符。
Term precedence
這實際上不真的是優先級, 但是它在這里是因為沒有操作符的優先級比 term 高. 查看 S02 獲取各種 terms 的更詳盡的描述. 這里有一些例子:
- Int 字面量
42
- Num 字面量
3.14
- 不能插值的 Str 字面量
'$100'
- 能插值的 Str 字面量
"Answer = $answer\n"
- 通用的 Str 字面量
q["$100"]
qq["$answer"]
- Heredoc
qq:to/END/
Dear $recipient:
Thanks!
Sincerely,
$me
END
- 數組構造器
[1,2,3]
[ ]
里面提供了列表上下文. 技術上講, 它實際上提供了一個 semilist
上下文, 即一系列分號分割的語句, 每條語句都在列表上下文中解釋, 然后被連接成最終的列表.
- 散列構造器
{ }
{ a => 42 }
{ }
里面要么是空的, 要么是以 pair 或 散列 開頭的單個列表, 否則你必須使用 hash( )
或 %( )
代替.
- Closure
{ ... }
如果出現在語句那兒, 會立即執行。 否則會延遲內部作用域的求值。
- 捕獲構造器
\(@a,$b,%c)
代表還不知道它的上下文的參數列表的抽取,
- 符號化變量
$x
@y
%z
$^a
$?FILE
&func
&div:(Int, Int --> Int)
- 符號作為上下文化函數
$()
@()
%()
&()
- quote-like 記號中的 Regexes
/abc/
rx:i[abc]
s/foo/bar/
- 轉換
tr/a..z/A..Z/
注意范圍使用 ..
而非 -
.
- 類型名
Num
::Some::Package
- 由圓括號環繞的子表達式
(1+2)
- 帶括號的函數調用
a(1)
一個項后面立即跟著一個圓括號化的表達式總是被當作函數調用, 即使那個標識符也含有前綴意義, 所以那種情況下你從來不用擔心優先級。因此:
not($x) + 1 # means (not $x) + 1
- Pair 構造器
:limit(5)
:!verbose
- 簽名字面量
:(Dog $self:)
- 使用隱式調用者的方法調用
.meth # call on $_
.=meth # modify $_
注意這只能出現在需要項(term)的地方。需要后綴的地方它就是后綴。如果需要中綴操作符(即, 在項后面, 之間是空格), .meth 就是語法錯誤。(.meth 形式在那兒是被允許的因為有一個和方法調用形式在語義上等價但是允許在 = 號和方法名之間于空格存在的特殊 .= 中綴賦值操作符)。
- Listop (leftward)
4,3, sort 2,1 # 4,3,1,2
就像 Perl 5 中一樣, 列表操作符對于它左側的表達式看起來像一個項(term), 所以它比左側的逗號綁定的緊湊點, 比右側的逗號綁定的松散點。-- 查看下面的列表前綴優先級。
方法后綴優先級
所有的方法后綴都以一個點開頭, 盡管對于下標來說, 點號是可選的. 因為這些是最緊密的操作符, 你可以看到一系列方法調用作為單獨的項, 這個項僅僅要表達一個復雜的名字.
- 標準的單個分發方法調用
$obj.meth
- 標準單個分發方法調用的變體
$obj.+meth
$obj.?meth
$obj.*meth
除了普通的 .
方法調用之外, 還有 .*
, .?
, 和 .+
變體來控制如何處理多個同名的相關方法.
- 類限定的方法調用
$obj.::Class::meth
$obj.Class::meth # same thing, 假設預先定義了 Class
就跟 Perl 5 一樣, 告訴分發器(dispatcher)從哪個類開始搜索, 而不正好是那個被調用的方法。
- 可變方法調用
$obj.=meth
.= 操作符執行了對左側對象的就地修改。
- 元方法調用
$obj.^meth
.^
操作符調用了類的元方法(class metamethod); foo.^bar 是 foo.HOW.bar
的簡寫。
- 像方法一樣的后環綴
$routine.()
$array.[]
$hash.{}
$hash.<>
$hash.??
不帶點的這些形式有同樣的優先級.
- 帶點形式的其它后綴操作符
$x.++ # postfix:<++>($x)
- 帶點形式的其它前綴操作符
$x.:<++> # prefix:<++>($x)
- 有一個特殊的非中綴操作符 infix:<.> 所以
$foo . $bar
總是會返回編譯時錯誤來標示用戶應該使用中綴操作符 infix<~> 代替。這用于捕獲正在學習 Perl 6 的 Perl 5 程序員可能會犯的錯誤。
自增優先級
就像在 C 中一樣,這些操作符增加或減少正在談論的那個對象的值,根據操作符在前面還是在后面。還是像 C 中一樣,在同一個表達式中多個對單個可變對象的引用可能導致未定義行為除非顯式地插入了某些序列操作符。請查看 "Sequence points"。
至于 Perl 6 中得所有后綴操作符,項(term) 和它的后綴之間不允許有空格。請查看 S02 來了解為什么,還有怎么使用 "unspace" 來應急這個約束。
和可變方法一樣,所有這些操作符被分派為操作符數的類型并返回一個同類型的結果,但是只有(不可變的)值存儲在可變容器中時,它們在值類型上才是合法的。然而,為了支持通用的習語,一個裸的未定義值(在一個合適的標量容器中)是被允許把自身修改成 Int 類型的:
say $x unless %seen{$x}++;
Str (在一個合適的容器中)的增加和 Perl 5 類似,但是被稍微推廣了一點。Perl 6 會掃描前面不是 '.' 字符的字符串中得最后的字母數字序列。不像 Perl 5 那樣,這個字母數字序列不需要錨定到字符串的開頭,也不需要以字母數字符開頭;字符串中匹配 <!after '.'> <rangechar>+
字母數字的最后的序列被增加而不管它前面是什么。
<rangechar>
字符類被定義為那種字符的子集(subset),Perl 知道怎么在范圍(range)中增加它,就像下面定義的那樣:
額外的匹配增加了兩個好處:對于典型的增長文件名的用法,你不必擔心路徑名或擴展名:
$file = "/tmp/pix000.jpg";
$file++; # /tmp/pix001.jpg, 不是 /tmp/pix000.jph
也許更重要的是, 如果你恰好增長了一個以小數結尾的字符串,Perl 6 也能應對自如:
$num = "123.456";
$num++; # 124.456, not 123.457
字符位置增加自然范圍內任何Unicode范圍被認為代表了數字0 . .9或被認為是一個完整的周期性字母的(一例)(Unicode)腳本。只在codepoints腳本,代表他們的字母表,形成一個周期獨立于其他字母可能使用。(此規范延緩這種腳本的用戶,以確定適當的周期的信件)。我們任意定義ASCII字母不相交與其他腳本,使用范圍的字符,但是字母點綴ASCII字母是不允許的。
如果在這樣一個范圍中當前字符是字符串位置中最后的字符,它包裝的第一個字符范圍和發送一個“攜帶”的位置了,然后那個位置是增加的范圍。當且僅當最左邊的位置是筋疲力盡的范圍,一個額外的字符相同的范圍是插入到持有套利以相同的方式作為Perl 5,所以遞增(zz99)“變成”(aaa00)zz和遞增(99)“變成”(100 aa)”。
> my $a = "99zz"
> $a++ # 99zz
> $a++ # 100aa
> my $b = 'zz99'
> $b++ # zz99
> $b++ # aaa00
下面的 Unicode 范圍是某些可能的 rangechar 范圍。對于字母我們有這樣的范圍:
A..Z # ASCII uc
a..z # ASCII lc
'Α'..'Ω' # Greek uc
α..ω # Greek lc (presumably skipping C<U+03C2>, final sigma)
?..? # 希伯來
etc. # (XXX out of my depth here)
> my @a = 'Α'..'Ω' # Α Β Γ Δ Ε Ζ Η Θ Ι Κ Λ Μ Ν Ξ Ο Π Ρ ? Σ Τ Υ Φ Χ Ψ Ω
對于數字我們有這樣的范圍:
0..9 # ASCII
?..? # 阿拉伯語-印度語
?..? # 天城文
?..? # 孟加拉語
'?'..'?' # 古木基文
?..? # 古吉拉特文
?..? # 奧里亞語
等等.
> my @b = '?'..'?' # ? ? ? ? ? ? ? ? ? ?
某些其它非書寫用的 0..9 范圍也可以被增長,例如:
?..? # 上標 (note, cycle includes latin-1 chars)
'?'..'?' # 下標
0..9 # 全角數字
> my @f = '?'..'?' # ? ? ? ? ? ? ? ? ? ?
Ⅰ..Ⅻ # clock roman numerals uc
ⅰ..? # clock roman numerals lc
?..? # circled digits/numbers 0..50
?..? # parenthesized lc
?..? # die faces 1..6
'?'..'?' # dingbat negative circled 1..10
etc.
注意: 對于 Perl 中實際的范圍, 你需要把上面的字符用引號括起來:
'?'..'?' # circled digits/numbers 0..50
my @d = '?'..'?'
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
my @e = '?'..'?' # ? ? ? ? ? ? ? ? ? ?
If you want to future-proof the top end of your range against further Unicode additions, you may specify it as "whatever":
'?'..* # circled digits/numbers up to current known Unicode max
prefix:<|>, 將對象展開為參數列表
| $capture
把Capture 值的內容(或 Capture-like)插值進當前的參數列表中, 就像它們被字面地指定那樣。
prefix:<||>, 將對象展開為分號列表
|| $parcel
把 Parcel(或其它有順序的值)的元素插值到當前參數列表中就像它們被字面地指定一樣, 由分號分割, 即, 以多維級別。在列表中的列表上下文之外使用該操作符是錯誤的; 換句話說, 它必須被綁定到 **
(slice)參數上而非吞噬參數*
上。
infix:<div>, 整除
$numerator div $denominator
> 3 div 2 # 1
> 13 div 2 # 6
而
> 13 div 2.4
報錯:
Cannot call infix:<div>(Int, Rat); none of these signatures match:
(Int:D \a, Int:D \b)
(int $a, int $b --> int)
in block <unit> at <unknown file>:1
infix:<%>, modulo
$x % $y
重復操作符
infix:<x>, 字符串/緩沖區 復制(或者重復/拷貝)
$string x $count
在字符串上下文中計算左邊的參數,重復結果字符串值由右側參數指定的倍數次,然后不管上下文,把結果作為單個連接好的字符串返回。
如果重復次數小于 1,則返回空字符串。重復次數不可以是 *
,因為 Perl 6 不支持無窮字符串。(至少,還沒有...)然而注意,有一天無窮字符串可能使用 cat($string xx *
) 來模仿,在這種情況下,$string x *
可能是它的簡寫。
'a' x *; # WhateverCode.new()
my $a = 'a' x *; # WhateverCode.new()
say $a; # WhateverCode.new()
say $a(12); # 可以傳遞參數!, 結果為 aaaaaaaaaaaa
infix:<xx>, 表達式重復操作符
@list xx $count # 如果 $count 是 * ,則返回一個無限列表 (懶惰的,因為列表默認是懶惰的 )
rand xx *; # infinite supply of random numbers
[ 0 xx $cols ] xx $rows # distinct arrays, not the same row replicated
> my @random= rand xx *;
> @random[0] # 0.510689533871727
> @random[0] # 0.510689533871727
> @random[1] # 0.993102039714483
> @random[2] # 0.177400471499773
> @random[12]
字符串連接
infix:<~>, 字符串 /緩沖 連接
$x ~ $y
范圍對象創建
$min .. $max
$min ^.. $max
$min ..^ $max
$min ^..^ $max
逗號操作符優先級
- infix:<,> 參數分隔符
1, 2, 3, @many
不像 Perl5 ,逗號操作符從來不返回最后一個值(在標量上下文中它返回一個列表)
- infix:<:>, 調用者標記
say $*OUT: "howdy, world" # howdy, world
say($*OUT: "howdy, world") # howdy, world
push @array: 1,2,3
push(@array: 1,2,3)
\($object: 1,2,3, :foo, :!bar)
冒號操作符就像逗號那樣解析,但是它把左邊的參數標記為調用者,這會把函數調用轉換為方法調用。它只能在參數列表或捕獲的第一個參數身上使用,用在其它地方會解析失敗。當用在捕獲中時,尚不知道?捕獲會被綁定到哪個簽名上;如果綁定到一個非方法的簽名身上,調用者僅僅轉換成第一個位置參數,就像冒號是一個逗號一樣。
為了避免和其它的冒號形式混淆,冒號中綴操作符后面必須跟上空格或終止符。它前面可以有空格也可以沒有空格。
注意:在下面把中綴操作符和冒號區別開:
@array.push: 1,2,3
@array.push(1,2,3): 4,5,6
push(@array, 1,2,3): 4,5,6
這是把普通函數或方法轉換為列表操作符的特殊形式。 這種特殊形式只在點語法形式的方法調用后被識別, 或者在方法或函數調用的右圓括號之后被識別。這種特殊形式不允許其間有空格,但是下一個參數的前面要有空格。如果可能的話在所有其它情況下,冒號會被解析為副詞的開始,否則會被解析為調用者標記(上面描述的中綴操作符。)
這種特殊形式不允許介于中間的空格, 但是允許在下一個參數之前有空格。 在所有情況下, 冒號會被盡可能地解析為副詞的開頭,或者調用者標記者(上面描述的中綴)
冒號的另一種特殊方式是, 允許正好在參數列表的右側圓括號之后為圓括號括住的參數列表添加 listop 參數,附帶條件是你被允許把 .foo(): 1,2,3
縮短為 .foo: 1,2,3
.(但是僅限于方法調用, 因為普通的函數不需要把處于第一個位置的冒號轉換為 listop, 空格就夠了。 如果你嘗試使用冒號擴展函數名最好把它看作標簽。)
foo $obj.bar: 1,2,3 # special, means foo($obj.bar(1,2,3))
foo $obj.bar(): 1,2,3 # special, means foo($obj.bar(1,2,3))
foo $obj.bar(1): 2,3 # special, means foo($obj.bar(1,2,3))
foo $obj.bar(1,2): 3 # special, means foo($obj.bar(1,2,3))
foo($obj.bar): 1,2,3 # special, means foo($obj.bar, 1,2,3)
foo($obj.bar, 1): 2,3 # special, means foo($obj.bar, 1,2,3)
foo($obj.bar, 1,2): 3 # special, means foo($obj.bar, 1,2,3)
foo $obj.bar : 1,2,3 # infix:<:>, means $obj.bar.foo(1,2,3)
foo ($obj.bar): 1,2,3 # infix:<:>, means $obj.bar.foo(1,2,3)
foo $obj.bar:1,2,3 # 語法錯誤
foo $obj.bar :1,2,3 # 語法錯誤
foo $obj.bar :baz # 副詞, means foo($obj.bar(:baz))
foo ($obj.bar) :baz # 副詞, means foo($obj.bar, :baz)
foo $obj.bar:baz # extended identifier, foo( $obj.'bar:baz' )
foo $obj.infix:<+> # extended identifier, foo( $obj.'infix:<+>' )
foo: 1,2,3 # label at statement start, else infix
這個故事的寓意是:如果你不知道冒號是怎樣結合的,就使用空格或圓括號讓它清晰。
- List infix precedence 列表中綴優先級
列表中綴操作符都有列表結合性,這意味著,同一個中綴操作符是同步起作用的,而不是一個接著一個。不同的操作符被認為是非結合性的,為了明確,必須用括號括起來。
- infix:<Z>, the zip operator
1,2 Z 3,4 # (1,3),(2,4)
> 2,5,7 [Zmin] 3,4,5 # 兩兩比較, 2 4 5
> my @a=3,6,9 # 3 6 9
> my @b=4,5,10 # 4 5 10
> @a [Zmin] @b # 3 5 9
> my @a = (1,2,9,3,5) # 1 2 9 3 5
> my @b = (2,3,5,1,9) # 2 3 5 1 9
> my @c = (2,3,4,5,1) # 2 3 4 5 1
> @a [Zmin] @b [Zmin] @c # 1 2 4 1 1
> @a [Zmax] @b [Zmax] @c # 2 3 9 5 9
- infix:<minmax>, minmax 操作符
@a minmax @b
返回@a和@b中最小值和最大值的一個范圍。
> my @a = 2,4,6,8;
> my @b = 1,3,5,7,9;
> @a minmax @b # 1..9
- infix:<X>, 交叉操作符
S03-metaops/cross.t lines 6–19
1,2 X 3,4 # (1,3), (1,4), (2,3), (2,4)
和 zip 操作符相比, X 操作符返回元素交叉后的列表。例如,如果只有 2 個列表,第一個列表中取一個元素和第二個列表中取一個元素組成 pair 對兒,第二個元素變化的最迅速。
最右邊的列表先遍歷完。因此, 你寫:
<a b> X <1 2>
你會得到:
('a', '1'), ('a', '2'), ('b', '1'), ('b', '2')
這在平的上下文會變成一個展平的列表,在 list of list 上下文中會變成列表中的列表
say flat(<a b> X <1 2>).perl # ("a", "1", "a", "2", "b", "1", "b", "2").list
say lol(<a b> X <1 2>).perl # (("a", "1"), ("a", "2"), ("b", "1"), ("b", "2"))
這個操作符是列表結合性的,所以:
1,2 X 3,4 X 5,6
生成
(1,3,5),(1,3,6),(1,4,5),(1,4,6),(2,3,5),(2,3,6),(2,4,5),(2,4,6)
另一方面,如果任一列表為空,你會得到一個空列表。
盡管X兩邊的列表可能是無限的,在操作符 X的右邊使用無限列表可能會產生意想不到的結果,例如:
<a b> X 0..*
會產生
('a',0), ('a',1), ('a',2), ('a',3), ('a',4), ('a',5), ...
并且你絕對不會到達 'b'。如果你左側的列表只包含單個元素,然而,這可能有用,尤其是如果 X 用作元操作符時。看下面。
say lol(<a b> X <1 2>).perl # ("a", "1", "a", "2", "b", "1", "b", "2")
Cross metaoperators 交叉操作符
@files X~ '.' X~ @extensions
1..10 X* 1..10
@x Xeqv @y
等等
一個常見的用法是讓一個列表只含有單個元素在操作符 X 的一邊或另一邊:
@vector X* 2; # 每個元素都乘以 2
$prefix X~ @infinitelist; # 在無限列表的每個元素前面前置一個元素
> my $prefix = ' - '
> my @a =<1 2 3 4 5>
> $prefix X~ @a # - 1 - 2 - 3 - 4 - 5
這時右邊有一個無限列表是可以的。