跳轉到

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}")