1987 Perl 誕生:Wall 的瑞士刀與「條條大路通羅馬」#
案發現場#
1980 年代中期,貝爾實驗室的系統管理員 Larry Wall 面臨一個日復一日的折磨:Unix 文字處理工具的碎片化。要在報告檔案中提取欄位、轉換格式、合併資料,得串起 sed、awk、grep、tr、shell 管線——每個工具各有自己的語法、自己的正則表達式方言、自己的極限。一旦任務複雜到需要變數、副程式、資料結構,工具鏈就崩潰,只能退回 C 重寫。
未解之謎是:能不能有一種語言,把 shell 的即時性、awk 的欄位處理、sed 的正則表達式、C 的完整控制,全部融為一體——一種「瑞士刀」語言?
Wall 嘗試用 awk 做一個報告產生器,發現它無法讀寫多個檔案句柄、沒有階層資料結構、正則引擎太弱。1987 年他發表 Perl 1.0,原名預留了「Pathologically Eclectic Rubbish Lister」的戲稱。這個問題為何重要?幾年之後,全球資訊網(WWW)爆發,動態網頁需要的就是文字處理——CGI 程式讀表單、生成 HTML、查資料庫。Perl 恰好是為此而生的語言,於是它成為了整個 Web 1.0 時代的霸主。
偵查過程#
Wall 的核心設計哲學可以濃縮為一個口號:TMTOWTDI(There's More Than One Way To Do It,條條大路通羅馬)。他認為語言應該像自然語言——允許多種表達方式、有慣用語(idiom)、有語境敏感性。技術推理如下:
第一步:正則表達式為一等公民。 Perl 把正則從外部工具升級為語言語法本身:
if ($line =~ /^From:\s*(.+)$/) {
my $sender = $1; # 捕獲群組直接變成變數
}
匹配運算子 =~、內建變數 $_(預設參數)、$1(第一捕獲群組)構成一套「語境驅動」的設計。Wall 的靈感來自自然語言學(他是語言學博士):語境決定語義——$_ 就像代詞,省略主詞就像中文的「吃過了嗎?」。
第二步:語境(context)系統。 Perl 獨創的設計:同一運算式在列表語境與純量語境中行為不同:
my @lines = <FILE>; # 列表語境:讀入所有行
my $n = @lines; # 純量語境:回傳行數
這讓函數可以「聰明地」適應呼叫者的需要,是自然語言式多義性的直接體現。
第三步:實用主義的語言集合。 Perl 融合了:
- shell 的 `$ 變數與即時執行
- awk 的 $_
、欄位分割、while (<>)` - sed 的正則替換
s/pattern/replacement/ - C 的完整控制結構與副程式
- Lisp 的參考(reference)實現階層資料結構
Wall 的名言:「Perl 的設計目標是讓簡單的事簡單,難的事可能。」它不追求優雅的一致性,只追求解決問題。
第四步:CGI 與生態。 1993 年 WWW 爆發,Perl 的 CGI 腳本成為動態網頁的標準做法;CPAN(Comprehensive Perl Archive Network,1995)建立了史上第一個大型開源模組庫,奠定了現代套件管理的範式。
結案報告#
Perl 的遺產:
- 腳本革命的推手:Perl 與 Python(1991-Python.md)、Ruby 並列三大腳本語言。Ruby 的設計者 Matz 明言 Perl 的靈活與 Smalltalk 的優雅是靈感來源。
- 正則表達式的普及:Perl 正則語法成為事實標準,被 Python、Java(1995-Java.md)、JavaScript(1995-JavaScript.md)、PCRE 吸收;「grep」與正則成為每個程式設計師的基本技能。
- CPAN 與開源文化:套件管理(PyPI、npm、RubyGems)的祖師爺。
- Web 1.0 的基石:CGI 腳本、早期的動態網站(Amazon 最初、IMDB、Slashdot)都以 Perl 寫成。
- 反向教材:Perl 的「魔力變數」與語境系統也成了可讀性差的反面教材,Python 的「There should be one obvious way to do it」正是對 TMTOWTDI 的直接回應。
Wall 至今仍領導 Perl 6(Raku)的發展。Perl 證明:語言可以像自然語言一樣——不求一致,但求好用。
證據與工具#
Perl 示範——正則、語境與報告處理:
#!/usr/bin/perl
use strict; use warnings;
my %count; # 階層資料結構(雜湊)
while (my $line = <>) { # <> 即時讀入
if ($line =~ /^(\w+):\s*(\d+)$/) { # 正則捕獲
$count{$1} += $2; # 欄位累加
}
}
for my $k (sort keys %count) { # TMTOWTDI 的慣用法
printf "%-10s %5d\n", $k, $count{$k};
}
# 輸入: apple: 3
# banana: 2
# apple: 4
# 輸出: apple 7
# banana 2
用 Python 模擬 Perl 的正則語境與 $_ 魔力變數:
import re, sys
class Perlish:
"""模擬 Perl 的預設變數 $_ 與語境行為"""
_ = None
@classmethod
def match(cls, pattern):
m = re.match(pattern, cls._ or "")
if m:
cls.groups = m.groups() # $1, $2 ...
return True
cls.groups = ()
return False
@classmethod
def s(cls, pattern, repl):
cls._ = re.sub(pattern, repl, cls._ or "")
return cls._
# while (<>) { if (/^(\w+):\s*(\d+)$/) { ... } }
count = {}
for line in ["apple: 3\n", "banana: 2\n", "apple: 4\n"]:
Perlish._ = line
if Perlish.match(r"^(\w+):\s*(\d+)$"):
key, num = Perlish.groups # 等同 $1, $2
count[key] = count.get(key, 0) + int(num)
for k in sorted(count):
print(f"{k:<10} {count[k]:>5}")