自動目錄
PERL的參照一直是迷一樣的存在,就算不會也寫得嚇嚇叫。為了讓本人更了解參照,我寫這篇文章。
參照又稱參考(reference),讓一個變數保存另一個變數的「記憶體位址」。
在變數前面加上一個反斜線 '\' 就會變成參照,主要有以下幾種:
1. 純量參照 $scalarref = \$foo;
2. 常數參照 $constref = \3.1415;
3. 陣列參照 $arrayref = \@ARGV;
4. 雜湊參照 $hashref = \%ENV;
5. 副程式參照 $coderef = \&handler;
6. 代號參照 $globref = \*STDOUT;
純量參照
$str = "一本書"; $ref = \$str; print $ref; # 得到一個位址例如 SCALAR(0x243a7b0) print $$ref; # 得到參照的值:一本書
說明
第2行 $ref 指向變數 $str的位址
第3行 印$ref 會印出一個位址,例如 SCALAR(0x243a7b0)
第4行 在參照前加上一個'$'字號,則印出該位址的值,這裡的兩個錢號要這樣看
想當然爾,當我們修改參照的目的變數時,一併會改變參照的值:
$str = "一本書"; $ref = \$str; print $$ref; #一本書 $str= "一隻狗"; print $$ref; #一隻狗
第4~5行 改變了原本的$str變數,結果參照的$$ref值也跟著改。
我們也可以直接改參照的值,原字串也會跟著改:
$$ref= "一頭牛";
print $str; # 一頭牛
常數參照
常數參照指向一個常數的位址,這個位址的值都是唯讀的
$ref = \3.14159; # 數值常數
$ref2 = \"ABC123"; # 字串常數
print $$ref; # 3.14159
如果企圖想要修改參照的值,就會報錯,例如:
$$ref= 2.732;
Modification of a read-only value attempted at ...
陣列參照
@arr = qw(orange apple lemon); $ref = \@arr; print @$ref; #orangeapplelemon print $arr[1]; #apple print $ref->[1]; #apple
說明
第3行 用 @$ref 印出陣列內容
第4行 原本印出陣列的第1個項目的方法
第5行 用參照印出陣列的第1個項目的方法,注意寫法
值得注意的,在上面的第2行把參照指到現存的陣列 @arr,這裡也可以直接給定陣列的內容,省略指定 @arr本身,這個稱為匿名陣列。
匿名陣列需要用中刮號 '[]' 來建立:
$ref = ['orange', 'apple', 'lemon' ];
print $ref->[1]; #apple
在 08-陣列 #2 --操作 中有提過,PERL的陣列都是一維結構,但是用參照的方法,可以建立多維的匿名陣列:
$ref = ['Mecury', 'Venus', ['Earth','Moon'], ['Mars', 'Phobos','Deimos']]; print "@$ref"; # Mecury Venus ARRAY(0x10d7a68) ARRAY(0x10f4030) print $ref->[1]; # Venus print $ref->[3][1]; # Phobos print $ref->[2][2]; # 不存在的參照會印出空字串 '' print $ref->[2]; # ARRAY(0x10d7a68)
第2行中陣列中的陣列會變成位址指標
注意第5行,如果該參照不存在,則印出空字串
雜湊參照
雜湊的參照和陣列參照大同小異,差別在於寫法:
%staff = ('name' => '王小明', 'age' => 33, 'job' => '工程師'); $ref = \%staff; print $staff{'name'}; #王小明 print $ref->{'job'}; #工程師 $ref->{'age'}=40; print $staff{'age'}; #40
同樣的,也可以直接用匿名雜湊,以大刮號 '{}' 建立
$ref = {'name' => '王小明', 'age' => 33, 'job' => '工程師'};
print $ref->{'job'}; #工程師
雜湊中也能建立雜湊,多維雜湊:
$ref = { 1=>{'name' => '王小明', 'age' => 33, 'job' => '工程師'}, 2=>{'name' => '李小華', 'age' => 44, 'job' => '業務'}, 3=>{'name' => '張大膽', 'age' => 22, 'job' => '實習生'}, 4=>['Sunday', 'Monday', 'Tuesday' ] }; print $ref->{3}->{'job'}; #實習生 print $ref->{3}{'job'}; #實習生 可以省略第二個箭頭 print $ref->{2}->{'addr'}; # 不存在的參照會印出空字串 '' print $ref->{4}->[2]; #Tuesday 雜湊中帶有陣列 print $ref->{4}[2]; #Tuesday 可以省略第二個箭頭
注意上面的範例中,雜湊中帶有陣列,第10行注意寫法。
副程式參照
print &power(2,4); #16 $ref = \&power; print &$ref(2,4); #16 sub power { $x = shift; $y = shift; return $x ** $y; }
上面的範列中撰寫了一個副程式 power來計算指數,雖然他放在下面,但不必擔心在前面叫用會出錯,因為perl會全部讀入編譯再執行。
第1行 傳統的副程式叫用方法
第3行 設定參照到此副程式
第4行 參照的叫用方法,注意寫法
副程式參照主要用於匿名副程式參照寫法,這樣可以直接把副程式指定給一個變數,在javascript中也有這樣的寫法。
$ref = sub { $x = shift; $y = shift; return $x ** $y; }; print &$ref(2,4); print &{$ref}(2,4); #結果同上,把參照用大刮號包起來
值得注意的就是如果這樣子寫,順序就很重要,先得要指定參照才能執行,像上面的第7行叫用放在 $ref指定前的話就會出錯。
代號參照
這參照很難理解,主要是對PERL中常用到的代號作參照,舉個例來說,執行時我要讓使用者輸入姓名:
print "輸入姓名:"; my $name = <STDIN>; chomp $name; #移除行尾換行 print "$name 您好!\n";
執行結果
輸入姓名:<等待輸入,輸入王大明>
王大明 您好!
用參照的寫法第2行改為:
$name= <$in>;
這個參照真是迷一樣的存在,我也不太理解他存在的意義。
副程式用變數傳參方法
接下來用範例來說明副程式用變數傳參方法。
假設某學校數學考試太難,老師討論後決定分數都開根號乘以10作為最後的成績,需要寫一個函數來回傳學生的最後的成績是否及格,同時也要把最後的成績回傳。
當然這個作法有很多種,例如把結果包成陣列回傳,這個範例使用傳參照變數的方法:
$math= 40; $pass= &adjscore(\$math); print $math . "\n"; #63.2455532033676 print "及格" if $pass ==1; print "不及格" if $pass ==0; sub adjscore { local $score = shift; $$score = sqrt($$score)*10; return 1 if $$score >=60; return 0; }
第3~4行 傳入的是分數參照,經過副程式演算後直接運算成新的成績
副程式中的變數 $score 接到的是 $math的參照,當然運算後直接就改成新的成績。
這就是參照好用的地方!
補充
foreach my $onekey ( keys $arr_ref) { .. }
當把參照放到迴圈中,新版的PERL出現這樣的錯誤:
Experimental values on scalar is now forbidden
如果是陣例參照,改成這樣之一:
foreach my $onekey ( keys @arr_ref) { .. }
如果是雜湊參照,改成這樣
上一篇 16-字串取代和置換
回到目錄 01-撰寫第一隻PERL程式
下一篇 18-套件及模組
參考資料
[1] https://perldoc.perl.org/perlref.html