Perl 6 語言概述
本文主要參考Learn X in Y minutes 翻譯而來:
Perl 6 是為未來百年而預備的充滿才華,特性豐富的程式語言。
Perl 6 現在的發行版 Rakudo 已經是文件豐富的發行版了。
注:本文程式碼的輸出以 #=>
開始。
註釋
單行註釋以井號開始:
# Single line comment start with a pound
多行註釋可以使用左對齊的 =begin flag
直到另外一行左對齊的 =end flag
橫跨多行
=begin comment
Mulitiline comment
start with =begin flag
end with =end flag
=end comment
變數
在 Perl 6 中,我們可以使用 my
來宣告一個區域性變數,用 our
來宣告一個全域性變數:
my $variable;
Perl 6 有四種型別的變數:
標量
通常表示單個的值。使用美元符號 $
開始:
my $str = 'String';
雙引號可以允許變數內插:
my $str2 = "This is $str";
變數名稱可以包含單引號 '
和減號 -
, 但不能以它們結尾:
my $weird'variable-name_ = 5; #=> 合法的變數名
Perl 6 的使用 True
和 False
表示布林值的真假:
my $bool = True;
我們可以使用前導的歎號 !
來對這個布林值取反:
my $inverse = !$bool;
將一個值轉換成布林值,使用前置的 so
:
my $forced-bool = so $str;
列表
列表代表多個值的集合,名字以 @
開始:
my @array = 'a', 'b', 'c';
my @array = ('a', 'b', 'c');
my @array = ['a', 'b', 'c'];
下面的寫法意思是一樣的:
my @letters = <a b c>; # 這和 Perl 5 的 qw, Ruby 的 %w 用法一樣
數字的列表:
my @array = 1, 2, 3;
陣列的索引從 0 開始,表示第三個元素用:
say @array[2];
陣列的內插用包括 []
的完整列表名稱:
say "Interpolate an array using [] : @array[]";
#=> Interpolate an array using [] : 1 2 3
給列表的索引重新賦值:
@array[0] = -1;
同時給多個索引賦值:
@array[0, 1] = 5, 6;
使用索引的列表給列表賦值:
my @keys = 0, 2;
@array[ @keys ] = @letters;
say @array; #=> a 6 b
雜湊
雜湊是鍵值對 Pair
的集合.
my %hash = 1 => 2, 3 => 4;
my %hash = (1 => 2, 3 => 4);
my %hash = { 1 => 2, 3 => 4 };
雜湊的鍵 key
如果沒有空格,可以不使用引號包圍:
my %hash = autoquoted => "key",
'some other' => 'value';
你可以直接用一個列表來給雜湊賦值:
my %hash = <key1 value1 key2 value2>;
這和下面的表示式是一樣的:
my %hash = key1 => 'value1', key2 => 'value2';
還有一種冒號對 colon pair
的簡潔語法來表示雜湊的鍵值對,常用在函式或方法的命名引數中:
my %hash = :w(1), # 等同於 w => 1
:truey, # 等同於 :truey(True) 或 truey => True
:!falsey, # 等同於 :falsey(False) 或 falsey => False
; # 在最後一個鍵值對後放置逗號是允許的
從雜湊中取出一個鍵的值使用 {}
:
say %hash{'key1'}; #=> value1
如果鍵 key
是由不包含空格的字元數字組成,則可以用 <>
這種更簡潔的寫法:
say %hash<key2>; # %hash{key2} 這種寫法是錯誤的
函式
函式就是大多數語言中的過程或方法。
sub say-hello { say "Hello, world" }
將一個函式儲存在變數中,我們用 &
開始的變數:
my &s = &say-hello;
可以將一個匿名函式儲存成變數:
my &other-s = sub { say "Anonymous function!" }
函式的引數可以宣告資料型別,這種宣告檢查在程式碼編譯的時候能有效阻止一些難以覺察的錯誤:
sub say-hello-to(Str $name) { say "Hello, $name!" }
引數還可以是可選的:
sub with-optional( $arg? ) { # 使用問號 `?` 來標記引數
say "I might return (Any) if I don't have an argument passed,
or I'll return my argument";
$arg;
}
呼叫這個函式可以使用下面幾種寫法:
with-optional; # 返回為空
with-optional(); # 返回為空
with-optional(1); # 返回 1
可以在引數中設定預設的值:
sub hello-to( $name = "World" ) { say "Hello, $name !" }
hello-to; #=> Hello, World !
hello-to(); #=> Hello, World!
hello-to('You'); #=> Hello, You !
當然可以用 冒號表示法
來書寫更簡潔的引數:
sub with-named( $normal-arg, :$named) { say $normal-arg + $named }
with-named(1, named => 6); #=> 7
with-named(1, :named(6)); #=> 7
和可選引數相對,強制的引數用 !
:
sub with-mandatory-named( :$str! ) { say "$str !" }
with-mandatory-named( str => "My String"); #=> My String !
當沒有引數或引數資料型別不對的時候,就會報錯:
with-mandatory-named;
#=> run time error: "Required named parameter not passed"
with-mandatory-named(3);
#=> run time error: "Too many positional parameters passed"
當然可以使用命名的布林引數:
sub takes-a-bool( $name, :$bool ) { say "$name takes $bool" }
takes-a-bool('config', :bool); #=> config takes True
takes-a-bool('config', :!bool); $=> config takes False
命名引數的預設值:
sub named-def( :$def = 5 ) { say $def }
named-def; #=> 5
named-def(def => 15); #=> 15
可以像 Perl 5 一樣,設定用星號 *
來表示 吞噬引數
(slurpy parameter) 來將後面的引數全部吸入:
sub as-many( $head, *@rest) { say @rest.join('/') ~ "!" }
say as-many(1, 'Happy', 'Birthday'); #=> Happy/Birthday!
用於引數的一個非常有用的操作符,|
, 通常用於展開列表:
sub concat-all( $a, $b, $c ) { say "$a $b $c" }
concat-all( |@array ); #=> a, b, c
像 Haskell 語言一樣,預設傳入 Perl 6 引數是被防寫的,不能修改:
sub immutate( $n ) { $n++ }
my $n = 1;
immutate($n); #=> error
當然也可以修改引數:
sub mutate( $n is rw ) { $n++ }
my $n = 1;
mutate($n);
say $n; #=> 2
如果不想被函式呼叫破壞變數的值,可以使用 copy
來對傳入的引數進行復制:
sub make-copy ($n is copy ) { $n++ }
my $n = 1;
make-copy($n);
say $n; #=> 1
條件語句
if True { say "It's true!" } #=> It's true!
unless False { "It's not false!" } #=> It's not false!
下面的寫法多此一舉,而且會報錯:
if (True) say 'It is true!";
也可以把條件語句放在表示式的後面:
say "Quite truthy" if True;
也可以用 Perl 5 的三元表示式來進行條件判斷,但是用 ?? !!
:
my $a = $condition ?? $value-if-true !! $value-if-false;
Perl 6 的 given when
組合已經被 Perl 5 借鑑了:
given 'foo bar' {
# $_ 的寫法和 Perl 5 是一樣的
say $_; #=> foo bar
when /foo/ { say "Yay!" } #=> Yay!
when $_.chars > 50 { say "Quite a long string!" }
default { say "Something else" } # 所有的 when 條件都不符合時,才觸發這個分支
}
迴圈結構
如果不設定條件或結束方法,loop
將會永遠的迴圈下去:
loop {
say "This is an infinite loop!";
last; # last 將會讓程式從迴圈中跳轉出去,就好像別的語言中的 break
}
loop (my $i = 0; $i < 5; $i++) {
next if $i == 3; # next 將會讓程式直接跳到下一次迴圈,類似別的語言中的 continue
say "This is a C-style for loop!";
}
for
語句接受一個列表,預設用 $_
來作為迭代引數:
for @array {
say "I've got $_";
.say; # 意思和 say $_; 相同
$_.say;
}
也可以設定有名迭代變數(區域性):
for @array -> $a {
next if $a == 3; # 跳轉到下次迴圈,迭代變數隨之變化
redo if $a == 4; # 將重新一次迴圈,迭代變數不變
last if $a == 5; # 結束迴圈
}
在 if
unless
等語句中也可以使用 ->
設定有名的區域性變數:
if long-computation() -> $result { say "The result is $result" }
for
迴圈結構可以遍歷巢狀的列表:
for 1, 2, (3, (4, ((5)))) {
say "Got $_.";
} #=> Got 1. Got 2. Got 3. Got 4. Got 5.
還有幾種形式的迴圈也經常用到:
map
常用於變換列表:
((1,2),3,(4,5)).map({ print "got $_," })
#=> got 1,got 2,got a,got 3,got 4,got 5,
pick
用於從列表中隨機獲取元素:
(@a, @b, @c).pick(1);
pick 1, @a, @b, @c; # 和上面的用處一樣
操作符 (Operators)
Perl 6 定義了豐富的操作符,這些操作符其實就是函式,但寫法上更隨意,可以有效增加程式碼的可讀性。
操作符分成 5 種:
- 前置型 (prefix), 如:
!
=>!True
- 後置型(posfix), 如:
++
=>$a++
- 中置型(infix),如:
*
=>4 * 3
- 環繞型(circumfix),如:
[
-]
=>[1, 2]
- 後置環繞型(post-circumfix),如:
{
-}
=>%hash{'key'}
比較操作符
==
!=
>
>
<=
>=
用於數字比較3 == 4;# False
3 != 4; # True
eq
ne
lt
le
gt
ge
用於字串比較'a' eq 'b'; # False
'a' ne 'b' # True
'a' !ne 'b'; # False
eqv
用於陣列的深度比較(1, 2) eqv (1, 3); # Flase
~~
是智慧匹配符,可用於多種型別的資料比較:'a' ~~ /a/; # True
'key' ~~ %hash; # 如果雜湊中包含名稱為 ‘key' 的鍵,則返回 True
$arg ~~ &bool-returning-function; # 如果函式可以接受 $arg 作為引數值,則返回真
1 ~~ Int; # 物件的例項可以和物件的型別進行匹配
1 ~~ True; # 數字和字串都可以和邏輯真假進行匹配
範圍構造符 ..
:
3 .. 7; # 3 到 7 所有的整數
^
在 ..
的任意一側,表示不包括這側的數:
3 ^..^ 7; # 4 到 6 所有的整數
^N
是 0 .. ^N
的縮寫:
^10; # 表示 0 到 9 所有的整數
..
同樣可以構造惰性(lazy
)和無限(infinite
)列表:
my @array = 1 .. *; # 1 .. INF 同樣是表示 1 到無窮的序列
可以將這種範圍列表作為列表的索引:
say @array[^10];
#=> 1 2 3 4 5 6 7 8 9 10
當你在 REPL 中打出 say 1..*
的命令時,Perl 6 將認為這是一個無限迴圈,
會強制在輸出一定數量後停止.
這種寫法在賦值時顯得格外簡潔:
my @numbers = ^20;
say @numbers;
#=> 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
邏輯操作符 and
or
(也可以寫做: &&
||
):
3 && 4; # 和 3 and 4 相同,返回 4
0 || False; # 和 0 or False 相同,返回 False
&&
也稱為 短路
操作符,返回第一個計算結果為 False 的值,或最後一個 True 值。
解包 (Unpacking)
這種能力在賦值運算中顯得簡潔:
my ($a, $b) = 1, 2;
say $a; #=> 1
my ($, $, $c) = 1, 2, 3; # $ 作為一個佔位符(place-holder)
say $c; #=> 3
my ($head, *@tail) = 1, 2, 3; # `吞噬符`在賦值中也能用
say @tail; #=> 2 3
在函式定義中,[..]
可以用於列表解包:
sub foo(@array [$fst, $snd]) {
say "My first is $fst, my second is $snd. All in all, I am @array[].";
}
foo([1,2]); #=> My first is 1, my second is 2. All in all, I am 1 2.
函式內部如果沒有 return
語句,那麼最後一個表示式的值將作為返回值:
sub next-index( $n ) { $n + 1 }
my $new-n = next-index(3); # $new-n 現在是 4
像大多數函式式語言一樣,Perl 6 的函式可以進行多次定義,每次的引數可以不同:
multi sub sayit(Int $n) { say "Number: $n" }
multi sub sayit(Str $s) { say "String: $s"; }
sayit("foo"); #=> String: foo
sayit(10); #=> Number: 10
sayit(True); #=> error! calling 'sayit' will never work with arguments of types
Perl 6 的函式還有很多特性,但不是很常用,詳細內容可到 http://doc.perl6.org 檢視。
變數生存期
和大部分動態語言類似,在使用變數前,需要先宣告它們.
Perl 6 定義了許多了宣告關鍵字,my
, our
, state
, temp
,
my
定義了一個區域性的變數,這種變數在塊結構中尤其有用。
my $foo = 'foo';
sub foo {
my $foo = 'Bar';
sub bar {
say "$foo $bar";
}
&bar; # 返回函式 bar 本身,並不會呼叫
}
foo()(); #=> 'Foo Bar'
從以上的程式碼中可以看出,變數 $foo
, $bar
保留了上級結構中定義的值。
當我們想在最外層使用 $bar
的時候,就會現實 undefined value
(未定義的值)。
Perl 6 有另外一種變數:動態變數(dynamic scope). 它們使用星號 *
開始.
my $*a = 1;
這種變數在 Lisp 的發展歷史中可以看到關於它的一些爭論,這種變數雖然不常用,但在某些場合 是必不可少的。
動態變數會在呼叫的時候才會從周圍環境去獲取當時的值,而不是在定義的時候就確定:
my $*foo = 1;
sub foo { say $*foo; }
動態變數是一種被函式呼叫的外部變數, 通常全域性變數都會被設定成動態變數。
our
定義的變數不是全域性的,而是包(package)範圍的。
temp
和 Perl 5 的 local
變數一樣,可以暫存上下文的變數,但不會修改它們。
state
宣告的變數,通常用在閉包或函式中:
my $hello = -> { state $var = 10; $var++ }
$hello(); #=> 10
$hello(); #=> 11
在閉包或函式中,靜態變數可以儲存呼叫過程中生成的中間結果,從而讓函式也可以擁有 狀態
.
物件模型
Perl 6 擁有相當複雜的物件模型。
我們可以用 class
來宣告一個類,用 has
定義一個屬性,類方法用 method
定義。
屬性用 $!attr
定義為私有,用 $.attr
定義的屬性是公共的。每一個公共的屬性都
會自動構造一個同名的類方法 attr
來獲取類的屬性值。
Perl 6 的物件模型叫 SixModel
是非常靈活的,可以允許我們動態的新增方法,改變類的定義。
class A {
# $.field 是不可變的
# 想從內部修改它,使用 `$!field`
has $.field;
# rw 可以宣告一個可以修改的類屬性
has $.other-field is rw;
# 內部屬性
has Int $!private-field = 10;
method get-value {
$.field + $!privated-field + $n;
}
method set-value($n) {
# $.field = $n; # 這種寫法是錯誤的
# 修改類屬性,應當用下面的寫法
$!field = $n;
# 類屬性是 rw 的話,就可以修改了
$.other-field = 5;
}
method !private-method {
say 'This method is private to the class!';
}
};
# 建立一個類屬性 `$.field` 為 5 的類例項
# 不能從外部修改私有屬性
my $a = A.new(field => 5);
$a.get-value; #=> 18
# $a.field = 5; 將會報錯,因為 $.field 是隻讀的
$a.other-field = 10; # 公共屬性是 rw 的,可以被修改
Perl 6 支援多重繼承,雖然很多人對此有爭論:
class A {
has $.val;
# submethod 定義的方法不會被繼承
submethod not-inherited {
say "This method could not be inherited";
say "submethod most useful for BUILD";
}
method bar { $.val * 5 }
}
# 從 A 繼承定義類 B
class B is A {
method foo { say $.val; }
# 將會覆蓋類 A 的同名方法 bar
method bar { $.val * 10 }
}
my $b = B.new(val => 5);
$b.not-inherited; # 將會報錯,因為這個方法不能繼承
$b.foo; #=> 5
$b.bar; #=> 50
角色(Roles)
Perl 6 的 Roles
類似 Ruby 的 Mixins
和 Java 的 Interface
,
是一種沒有例項的抽象類:
role PrintableVal {
has $!counter = 0;
method print {
say $.val;
}
}
想要從 Role
中繼承介面,就用 does
:
class Item does PrintableVal {
has $.val;
# 從 Role 繼承後,Roles 中所有的私有屬性和方法都是可見的
method access {
say $!counter++;
}
}
正規表示式
Perl 6 的正規表示式 Regex
是 Perl 5 的正規表示式 Regexp
的擴充套件,增加了很多有用的功能。
有一些東西沒有變,例如 ?
, +
和 *
, 但有些東西就不同了,例如 |
的行為。
正規表示式中任意位置可以增加一些空格以增加程式碼的可讀性:
say so 'a' ~~ /a/; #=> True
say so 'a' ~~ / a /; #=> True
所有的正規表示式都會返回一個 Match
物件,這個物件可以像列表一樣取索引,也可以像
雜湊一樣查詢指定名稱的值,也可以返回字串格式的匹配結果。
返回的匹配結果儲存在一個區域性變數 $/
中,你可以使用 $0
, $1
, ... 來獲取捕獲的值。
在 Perl 6 中,任何除了字母之外的字元都可能是含義不同的字元,所以如果想表示字元本身,
最好使用引號或者將其使用反斜槓 \
轉義:
say so 'a|b' ~~ / a '|' b /; # True
say so 'a|b' ~~ / a \| b /; # True
預設在 Regex
中,空格是被忽略的,如果想讓空格恢復其本意,要使用 :s
:
say so 'a b c' ~~ / a b c /; # `False`
say so 'a b c' ~~ /:s a b c /; # `True`
數量表示符
?
表示匹配 0 或 1 個:
say so 'ac' ~~ / a b? c /; # `True`
say so 'abc' ~~ / a b? c /; # `True`
+
表示匹配 1 或多個:
say so 'ac' ~~ / a b+ c /; # `False`
say so 'abc' ~~ / a b+ c /; # `True`
say so 'abbbbc' ~~ / a b+ c /; # `True`
*
表示匹配 0 或多個:
say so 'ac' ~~ / a b* c /; # `True`, they're all optional.
say so 'abc' ~~ / a b* c /; # `True`
so 'abbbbc' ~~ / a b* c /; # `True`
so 'aec' ~~ / a b* c /; # `False`. "b"(s) are optional, not replaceable.
**
表示指定數量範圍的個數
say so 'abc' ~~ / a b ** 1 c /; # `True`
say so 'abc' ~~ / a b ** 1..3 c /; # `True`
say so 'abbbc' ~~ / a b ** 1..3 c /; # `True`
say so 'abbbbbbc' ~~ / a b ** 1..3 c /; # `False`
say so 'abbbbbbc' ~~ / a b ** 3..* c /; # `True`
<[]>
表示字元類
字元類相當於 Perl 5 的 []
,但這種寫法可以讓字元類進行一些簡單的運算:
say 'fooa' ~~ / f <[ o a ]>+ /; #=> 'fooa'
字元類中用 ..
表示範圍,就像 PCRE 中的破折號 -
:
say 'aeiou' ~~ / a <[ e..w ]> /;
#=> 'aeiou'
除了 a..z A..Z 0..9
外的所有字元,都需要轉義才能表示其本身, 包括空格:
say 'he-he !' ~~ / 'he-' <[ a..z \! \ ]> + /;
#=> 'he-he !'
如果將重複的字元放在一個字元類中,就會報錯:
'he he' ~~ / <[ h e ' ' ]> /;
#=> Warns "Repeated characters found in characters class"
想要對字元類取反,用 <-[...]>
(PCRE 中用 [^]
):
say so 'foo' ~~ / <-[ f o ]> + /;
#=> False
在字符集之間,-
表示差集,+
表示並集:
say so 'foo' ~~ / <[ a..z ] - [ f o ]> + /;
#=> False
say so 'foo' ~~ / <-[ a..z ] + [ f o ]> + /;
#=> True
say so 'foo!' ~~ / <-[ a..z ] + [ f o ]> + /;
#=> True
分組和捕獲
分組:我們可以用 []
將正規表示式分組( 像 Perl 5 中的 (?:)
:
say so 'abc' ~~ / a [ b ] c /; # `True`
say so 'fooABCABCbar' ~~ / foo [ A B C ] + bar /; #=> True
捕獲:我們可以用 ()
來捕獲匹配到的字串:
say so 'fooABCABCbar' ~~ / foo ( A B C ) + bar /;
#=> `True`
匹配結果儲存在區域性變數 $/
中,可以像列表一樣使用:
say $/[0];
#=> 「ABC」 「ABC」 (這是 `Match` 物件的表示形式)
say $0; #=> 「ABC」 「ABC」 (同上)
分支匹配符 |
和 ||
:
||
分支符同 PCRE 的 |
的效果相同,都是取第一個符合條件的分支:
'foo' ~~ / (fo || foo) /; say $0; #=> 「fo」
|
分支符是別的語言沒有的,它將選擇所有匹配分支中最長的字串的那個分支:
'foo' ~~ / (fo | foo) /; say $0; #=> 「foo」
包(package)
包是組織程式碼的形式,是一種名稱空間(namespace), Perl 6 一共有 6 種形式的包, 分別是 class
, module
, regex
, role
, subset
和 enum
. package
是最底層的實現。
class Package::Name::Here {}
module Hello::World { }
module Parse::Text; # 通常位於檔案第一行
grammar Parse::Text::Grammar { }
呼叫一個類,模組,可以用 use
:
use JSON::Tiny; # 預設會匯出 from-json 這個函式
say from-json('[1]').perl; #=> [1]
當然你也可以用下面的方式呼叫:
my $actions = JSON::Tiny::Actions.new;
所有的 package 型別(class
, role
, etc) 預設都是全域性的,當然你也可以定義區域性的包:
定義一個可以匯出變數和方法的模組:
module Foo::Bar {
our $n = 1;
our sub inc {
our sub available {
say "Don't do that. Seriously. You'd get burned.";
}
my sub unavailable {
say "Can't access me from outside, I'm my !";
}
}
say ++$n;
}
say $Foo::Bar::n; #=> 1
Foo::Bar::inc; #=> 2
Foo::Bar::inc; #=> 3
常量
可以用 constant
定義一個全域性的常量:
constant Pi = 3.14;
constant $var = 1;
(完)
相關文章
- 6,雜湊(perl語言筆記)筆記
- perl語言入門
- Perl語言學習(四)Perl控制結構
- Java語言概述Java
- C語言概述C語言
- Larry Wall 專訪:語言學、Perl 6 的設計和釋出
- perl語言程式設計 下載程式設計
- C++語言概述 (轉)C++
- Flutter系列之Dart語言概述FlutterDart
- 自然語言處理(NLP)概述自然語言處理
- JSP 表示式語言概述JS
- go語言編譯過程概述Go編譯
- Java開發之路—java語言概述Java
- 第二章 C語言概述C語言
- 5,輸入與輸出(perl語言筆記)筆記
- 學習perl(6)
- Perl語言的文書處理模式之一(轉)模式
- Perl語言的文書處理模式之二(轉)模式
- Perl語言的文書處理模式之三(轉)模式
- [譯][Perl 6] 5to6-perlfunc
- [譯][Perl 6] 5to6-nutshell
- 《Apache服務之php/perl/cgi語言的支援》RHEL6——服務的優先順序ApachePHP
- Perl 6 網頁抓取網頁
- perl6學習
- Java 語言概述與開發環境(1)Java開發環境
- Java 語言概述與開發環境(2)Java開發環境
- 02-Java語言概述及環境配置Java
- 安全是一門語言的藝術|威脅調查分析語言概述
- Perl常用語法
- Perl 語言程式設計 第三版 線上版本程式設計
- Linux指令碼語言PERL 的模板應用分析(轉)Linux指令碼
- Rust 語言學習之旅(6)Rust
- C_Primer第2章 C語言概述C語言
- Go 語言程式碼風格規範-概述篇Go
- 語言模型文字處理基石:Tokenizer簡明概述模型
- 在Linux系統下安裝Perl指令碼語言(轉)Linux指令碼
- 《C語言程式設計:問題與求解方法》——2.2節C語言歷史概述C語言程式設計
- C語言模擬試題6C語言