語言基本要素
加法範例程式
# 數字版 # for 迴圈版 (自找麻煩版) print "13+5=", 13+5, "\n"; use strict; my (@x, $sum, $i); # 純量變數版 @x = (13, 5, 1, 7); $x = 13; $sum = 0; $y = 5; for ($i=0; $i<=$#x; ++$i) { print $x + $y, "\n"; $sum += $x[$i]; } # 變數宣告版 print $sum, "\n"; use strict; my ($x, $y); # 好用版: 數字從命令列參數讀入 $x = 13; print $ARGV[0] + $ARGV[1], "\n"; $y = 5; print $x + $y, "\n"; # 實用版: 讀命令列上所有的參數 use strict; # 陣列版 my ($sum, $t); use strict; $sum = 0; my (@x, $sum, $t); foreach $t (@ARGV) { @x = (13, 5, 1, 7); $sum += $t; $sum = 0; } foreach $t (@x) { print "$sum\n"; $sum += $t; } print "$sum\n";
簡單的加法程式
剛開始學一種語言, 請自己用手照著打一遍。 (嘿嘿嘿, 上面程式的排版方式, 就是故意不讓讀者輕易剪貼... 除非您會用 vim 的 visual block mode 。) 如果出現錯誤訊息, 不要只是把程式改對就算了, 更一定要注意看 錯誤訊息 說什麼。 這樣以後遇到相同的錯誤訊息, 你才能很快地想起來是不是少打了分號還是變數前面忘記加錢號。
數字版心得:
- 從 "#" 開始, 一直到該列結束, 都是註解。
- 放在雙引號裡面的是字串, 會原封不動地印出來。
- print 後面可以接一串東西, 用逗點分開。 如果你自己沒有在字串頭尾加空格, 印出來的結果就會黏在一起。 print 不會自動幫你加空格。
- 每句話都以分號結尾。
純量變數版與變數宣告版心得:
- 在 perl 裡面, 變數前面都要加錢號。 這種簡單的變數叫做 純量變數 scalar variable。
- 原本 perl 允許你直接使用變數, 不必宣告。 不過良藥苦口利於病, 忠言逆耳利於行 -- 與其等到執行的時候才錯得不明不白, 還不如在直譯時就多聽 perl 嘮叨一些, 至少它印的錯誤訊息會告訴你第幾行出錯 ( " ... at 你的程式檔名 line 6")
- 加上
use strict;
之後, perl 就會要求使用變數之前必須宣告。 - 用
my (...);
宣告變數。
陣列版心得:
- 以小老鼠開頭的變數是 陣列變數 array variable。 注意它設定初始值的方法與 print 後面放的東西類似 -- 都是一串東西用逗點分開。 只不過外面必須多一對括弧。 (其實 print 後面那一串東西, 外面多包一對括弧一樣也可以。)
- 如果你心裡想的是 "我要把陣列 @x 裡面的每個元素 $e 都拿來..."
那就直接翻譯成
foreach $e (@x) { ... 處理 $e ... }
這樣迴圈每執行一次, @x 裡面的每個元素就會輪流變身成為 $e 讓你在迴圈裡面處理。 $sum += $t
其實就是$sum = $sum + $t
- 其實雙引號字串 "..." 裡面的東西, 並非完全原封不動地印出。 例如 "\n" 會印換列; 而字串裡面如果有純量變數, 則會把變數的值代換進去。 請試試看將 print 後面的字串改為單引號字串 '...' 看會怎麼樣。
自找麻煩版心得: 熟悉 C 的同學可能比較習慣用 for 迴圈。 這個 perl
也有, 而且語法和 C 一模一樣: for (初始設定; 應該繼續;
準備下一步) { ... }
效果相當於:
初始設定; while (應該繼續) { ... 準備下一步; }
但如果要這樣寫, 你就必須知道迴圈要做幾次。 陣列 @x 的元素個數是 $#x + 1 也就是說, $#x 是陣列 @x 最後一個元素的註標 (因為 perl 與 c 一樣, 從第 0 個元素開始數起。)
結論是: 可以很自然使用 foreach 的場合, 就不要自找麻煩使用 for。 foreach 比較簡潔, 又更能夠口語化地直接反應出我們心中想的邏輯。 寫程式之前先將你想做的事用中文說出來, 如果你聽到自己喃喃唸著 "把陣列當中的每個元素抓出來...", 那麼就可以直接翻譯成 foreach 語法。
正因為 perl 程式念起來很像英文 (畢竟發明人 Larry Wall 是語言學家嘛), 也就很可以想像為什麼網路上有人 拿 perl 作詩, 甚至舉辦 perl 寫詩比賽。
當然, for 迴圈的彈性比 foreach 彈性大, 有些場合就是很難套用
foreach, 例如 f-f 乘法表 就用 for
比較自然。 而 foreach 能做的事, 也都必然可以用 for 做得到:
在簡單的情況下 (也就是說, 嚴格說起來以下並不正確), 如果你只從 @all
讀資料出來, 而不去修改它的內容, 則 foreach $var (@all) { ...
}
的效果相當於:
for ($i=0; $i<=$#all; ++$i) { $var = $all[$i]; ... }
好用版與實用版心得: 你自己寫的 perl 程式也可以像系統命令一樣帥,
可以從命令列上讀參數進去, 像這樣: ./sum 5 18 2 7 9
於是
perl 會自動將用你程式的人所敲進去的 命令列參數 command line
argument 放入 @ARGV 陣列 (注意大小寫! 在 perl
裡面大小寫有分別!), 你從程式裡面就可以抓出來處理。 這個陣列是內建的,
不必宣告就可以用。
計算 log 的近似值
下面這個程式計算 log 的近似值, 例如 ./log 128
會印 7,
而 ./log 4096
會印 12。 它內定以 2 為底;
不過如果命令列上出現第二個參數, 就改以第二個參數為底, 例如
./log 4096 8
會印 4, 而 ./log 243 3
會印
5。
#!/usr/bin/perl -w use strict; my ($base, $ans) = (2, 0); if ($#ARGV > 1 or $#ARGV < 0) { die "usage: log number [base]\n"; } elsif ($#ARGV == 1) { $base = $ARGV[1]; } my ($n) = $ARGV[0]; while ($n > 1) { $n /= $base; ++$ans; } print "log($ARGV[0]) base $base is roughly $ans\n";
心得:
- 變數可以在宣告的同時, 一併設定初始值。 注意等號右邊的語法, 是不是和陣列設定初始值很像?
- 邏輯判斷 if-then-else, 在 perl 裡面當然也可以用。 注意 perl 多了一種子句 "elsif" 讓程式看起來比較簡單。 如果是在 c 裡面, 這必須拆成 "else { if ... }" 又多了一層縮排, 看起來比較複雜。
- 熟悉 c 的同學請注意: 即使大括弧裡面只有一句話, 大括弧還是一樣不可以省略。 這一點看起來好像輸給 c; 不過 perl 有其他省略的方法。
- 如果出現使用者輸入資料錯誤等狀況, 程式做不下去了, 可以用 die 結束程式。 記得印出有用的錯誤訊息。
- 隨時隨地都可以宣告變數, 也不限一次。
有關變數
Perl 裡面的變數不分整數/浮點數/字元/字串/..., 一律都叫做
scalar 純量. Perl 是我們肚子裡的蛔蟲,
它會觀察你想對這個純量變數做什麼運算, 根據你呼叫的 function
函數 或你用的 operator 運算子
來決定要把這個純量變數當做數字還是字串. 例: 請在上面範例中的
"純量變數版" 兩個數字外面加上引號, 像這樣: $x = "13";
看看結果有何不同. 例: 請在 "陣列版" 的最後面加上一句 print
length($sum), "\n";
看看是否會出現錯誤訊息.
Perl 裡面的變數, 乃是根據複雜程度 -- 儲存資料的多寡來分類.
- 以 $ 開頭的是 scalar variable 純量變數. 一個純量變數可以放一個數字或一個字串.
- 以 @ 開頭的是 array 陣列, 一個陣列變數 @x
裡面放著很多個純量, 從第 0 號按順序排到第 $#x 號。 Q:
如何判斷一個陣列變數有沒有元素? 請對 @ARGV 作實驗, 驗證你的假說。
可以這樣子填初始值:
@x = ("guava", "pineapple", "banana");
而用 $x[0] 或 $x[1] 或 ... 或 $x[$#x] 存取 @x 的個別元素: - 以 % 開頭的是 hash (雜湊?) 在其他語言中, 稱為
associated array 關聯陣列. 一個 hash %x 裡面放著很多對純量
(比陣列厲害吧), 沒有任何次序地散成一堆. 每一對純量當中, 一個叫 key,
功用是搜尋資料; 另一個叫 value, 它才是真的要存取的資料本身.
可以這樣子填初始值:
%x = ("pig"=>98.3, "cat"=>2.5, "dog"=>5.3");
而用 $x{"pig"} 或 $x{"cat"} 或 ... 存取 %x 的個別元素: 可以把 hash 想成是一個對照表. 也可以將它想成是具有特異功能的陣列: 一般的陣列用整數作為註標; 而 hash 則可以用任何字串作為註標.
注意:
- $speed 是一個純量變數; $speed[3] 也是一個純量變數, 但它和 $speed 無關, 它是 @speed 陣列內的一個元素; $speed{"bike"} 又是另一個和 $speed 無關的純量變數, 它是 %speed 這個 hash 內的一個元素.
- 為 array 和 hash 設定初始值時, 等號右邊都是 小括弧! 用小括弧括起來的東西叫做 list, 以後再詳述.
更詳細的討論, 請見 perldata(1) (<== 這個的意思是下
man 1 perldata
)
其他常識
-
在雙引號內, perl 還是認得純量變數與陣列變數 (但不認得 hash)
會把它的值代換進去. 例如
$name = "kitty"; @animals = ("dog", "cat", "sheep", "fish"); %x = ("pig"=>98.3, "cat"=>2.5, "dog"=>5.3); print "$name, @animals, %x\n"; print @animals, "\n";
- C 語言當中常用的數學運算子, 在 perl 當中都可以用, 例如 +, -, *, /, %, <<, >>, ++, --, < <=, > >=; ==, !=, ... 注意: 比較數字是否相等, 要用 == 而比較字串是否相等, 要用 eq
- 邏輯運算子 and/or/not, 可以用 c 的語法 &&/||/!, 也可以用口語化的寫法。 雖然三個看起來像符號; 三個看起來像名字, 但其實都是 運算子 operator, 唯一的差別是優先順序高低不同。 (前三者低, 後三者高)
- 為了方便除錯, 習慣上在呼叫 perl 時加上 -w 參數, 要 perl 多產生一些警告訊息; 同時要記得 use strict 。
- 建議使用 Linux 環境的讀者, 學會使用 readline 與 less, 非常有助於加速操作.
作業
- 請將 "純量變數版" 裡面的
... $x + $y ...
故意改成錯誤的... $x + $z ...
。 然後不用 -w 執行一次; 再用 -w 執行一次。 程式有沒有執行並印出和? 有沒有印出錯誤訊息? - 同樣故意將 "變數宣告版" 裡面的 $y 改成 $z。 同樣不用 -w -w
執行一次; 再用 -w 執行一次。 這次程式有沒有執行並印出和?
有沒有印出錯誤訊息? 呢? 又請這樣執行 "好用版" :
perl -w test.pl 13
看看發生什麼事? 請查字典解釋印出來的錯誤訊息。 - 從命令列上輸入 m 與 n 兩個數字, 計算 m 的 n 次方, 像這樣:
./power 6 3
印出 216 而./power 2.5 -2
印出 0.16 。 可以假設 n 一定是整數 (但可能是正整數或負整數)。 (暫時不准用 ** 這個運算子) -
把命令列上所有奇數加總; 又把命令列上所有偶數加總;
同時還要數數看命令列上出現幾個 0, 像這樣:
./o_e_sum 5 12 6 0 8 7 1 4 0
印出sum of odd numbers: 13 sum of even numbers: 30 number of zeros: 2
-
把命令列上第 0,2,4,6,8, ... 個參數相加; 又把命令列上第 1,3,5,7,9,
... 個參數相加, 像這樣:
./alt_sum 5 12 6 0 8 7 1 4 0
印出sum of all numbers at even positions: 20 sum of all numbers at odd positions: 23
提醒: 不習慣寫程式的同學, 千萬不要眼高手低, 貪心想要一口氣完成。 要養成腳踏實地的習慣, 先把題目先一路簡化到到自己可以處理的程度, 再逐次增加功能。 寫一點, 就測試一下, 比較容易除錯, 也比較容易有成就感。
- 不會分奇偶數求和? 先退一步想, 至少可以先求所有元素的和吧?
- 不會求所有元素的和? 再退一步想, 至少可以先把讀進去的資料全部原封不動地印出來吧?
- 不會讀資料? 更退一步想, 至少可以把資料寫死, 從一個固定的陣列裡面抓出來求和吧? (像陣列版)
而且天下程式一大抄, 將問題簡化夠了, 就很容易找到類似的範例程式, 從原本就可以動的程式開始改起, 怎麼可能交白卷呢?
- 本頁最新版網址: https://frdm.cyut.edu.tw/~ckhung/b/pl/basic.php; 您所看到的版本: February 14 2012 10:32:25.
- 作者: 朝陽科技大學 資訊管理系 洪朝貴
- 寶貝你我的地球, 請 減少列印, 多用背面, 丟棄時做垃圾分類。
- 本文件以 Creative Commons Attribution-ShareAlike License 或以 Free Document License 方式公開授權大眾自由複製/修改/散佈。