Icon語言

**Icon** 是一門領域特定的高階程式語言，有著「目的（goal）導向執行」特徵，和操縱字串和文字模式的很多設施。它衍生自 SNOBOL 和 SL5 字串處理語言。Icon 不是物件導向的，但在 1996 年開發了叫做 Idol 的物件導向擴充，它最終變成了 Unicon。

## 歷史

在 1971 年 8 月，SNOBOL 的設計者之一 Ralph Griswold 離開了貝爾實驗室，成為了亞利桑那大學的教授。他那時將 SNOBOL4 介入為研究工具。

作為最初在 1960 年代早期開發的語言，SNOBOL 的語法帶有其他早期程式語言的印記，比如 FORTRAN 和 COBOL。特別是，語言是依賴列的，像很多要錄入到打孔卡的語言一樣，有著列布局是很自然的。此外，控制結構幾乎完全基於了分支，而非使用塊，而塊在 ALGOL 60 中介入之後，已經成為了必備的特徵。在他遷移到亞利桑那的時候，SNOBOL4 的語法已然過時了。

Griswold 開始致力於用傳統的流程控制結構如 `if…then`，來實現 SNOBOL 底層的成功和失敗概念。這成為了 SL5，即「SNOBOL Language 5」的簡寫，但是結果不令人滿意。在 1977 年，他考慮設計語言的新版本。他放棄了在 SL5 中介入的非常強力的函式系統，介入更簡單的暫停和恢復概念，並為 SNOBOL4 自然後繼者開發了新概念，具有如下的原則：

* SNOBOL4 的哲學和語意基礎；
* SL5 的語法基礎；
* SL5 的特徵，排除廣義的過程機制。

新語言最初叫做 SNOBOL5，但因為除了底層概念外，全都與 SNOBOL 有著顯著的差異，最終想要一個新名字。在這個時候 Xerox PARC 出版了他們關於圖形化使用者介面的工作，術語「icon」從而進入了電腦詞彙中。起初確定為「icon」而最終選擇了「Icon」。

## 基本語法

Icon 語言衍生自 ALGOL 類的結構化編程語言，因而有著類似 C 或 Pascal 的語法。Icon 最類似於 Pascal 的，是使用了 `:=` 語法的賦值，`procedure` 關鍵字和類似的語法。在另一方面，Icon 使用 C 風格的花括號來結構化執行分組，並且程式開始於執行叫做 `main` 的過程。

Icon 還在很多方面分享了多數手稿語言（還有 SNOBOL 及 SL5）的特徵：變數不需要聲明，類型是自動轉換的，就說數字和字串可以自動來迴轉換。另一個常見於很多而非全部的手稿語言的特徵是，缺少行終止字元；在 Icon 中，不結束於分號的行，若其確有意義則由暗含的分號來終結。

過程是 Icon 程式的基本建造塊。儘管它們使用 Pascal 名稱，但工效上更像 C 函式並可以返回值；在 Icon 中沒有 `function` 關鍵字。

## 目的導向執行

Icon 的關鍵概念之一就是其控制結構基於表達式的「成功」或「失敗」，而非大多數其他程式語言中的布林運算。這個特徵直接衍生自 SNOBOL，在其中表達式求值、模式匹配和模式匹配連帶替換，都可以跟隨著成功或失敗子句，用來指定在這個條件下要分支到一個語句標籤。例如，下列代碼列印「Hello, World!」五次：

要進行迴圈，在索引變數 `I` 之上呼叫內建的函式 `LE`（小於等於），並且如果 `S`（成功），意味著 `I` 小於等於 `5`，它分支到命名標籤 `LOOP` 並繼續下去。

Icon 保留了基於成功或失敗的控制流程的基本概念，但進一步發展了語言。一個變更是將加標籤的 `GOTO` 式的分支，替代為面向塊的結構，符合在 1960 年代後期席捲電腦工業的結構化編程風格。另一個變更是允許失敗沿著呼叫鏈向上傳遞，使得整個塊作為一個整體的成功或失敗。這是 Icon 語言的關鍵概念。而在傳統語言中，必須包括基於布林運算的測試成功或失敗的代碼，並接著基於產出結果進行分支，這種測試和分支是原生於 Icon 代碼的，而不需要明確的寫出。考慮如下複製標準輸入到標準輸出的簡單代碼：

它的含義是：「只要讀取不返回失敗，呼叫寫出，否則停止」。在 Icon 中，`read()` 函式返回一行文字或 `&fail`。`&fail` 不是簡單的 Java 中的特殊返回值 `EOF`（檔案結束）的類似者，因為它被語言依據上下文明確理解為意味著「停止處理」或「按失敗狀況處理」。這裡即使 `read()` 導致一個錯誤它都會工作，比如說如果檔案不存在。在這種情況下，語句 `a := read()` 會失敗，而寫操作簡單的不呼叫。

成功和失敗將沿著呼叫鏈向上傳遞，意味著可以將函式嵌入其他函式內，在巢狀函式失敗時，它們整體停止。例如，上面的代碼可以精簡為：

在 `read()` 命令失敗的時候，比如在檔案結束之處，失敗將沿著呼叫鏈上傳，而 `write()` 也會失敗。`while` 作為一個控制結構，在失敗時停止。Icon 稱謂這個概念為「目的導向執行」，指稱這種只要某個目的達到執行就繼續的方式。在上面的例子中目的是讀整個檔案；讀命令在有資訊讀到的時候成功，而在沒有的時候失敗。目的因此直接編碼於語言中，不用再去檢查返回碼或類似的構造。

Icon 使用目的導向機制用於進行傳統的布林測試，儘管有著微妙的差異。一個簡單的比較如 `if a < b then write("a is smaller than b")`，這裡的 `if` 子句，不像在多數語言中那樣意味著：「如果右側運算求值為真」；轉而它的意味更像是：「如果右側運算成功」。在這種情況下，如果這個比較為真，`<` 算子成功。如果 `if` 子句的這個表達式成功，則呼叫 `then` 子句，如果它失敗了，則呼叫 `else` 子句或下一行。結果同於在其他語言中見到的傳統 `if…then`，如果 `a` 小於 `b`，`if` 進行 `then` 子句。微妙之處是相同的比較表達式可以放置在任何地方，例如：

另一個不同是 `<` 算子如果成功，返回它的第二個實際參數，在這個例子中，如果 `b` 大於 `a`，則導致它的值被寫出，否則什麼都不寫。因為並非測試本身，而是一個算符返回一個值，它們可以串聯在一起，允許像 `if a < b < c` 這樣的事情 ，在多數語言中平常類型的比較下，必須寫為兩個不等式的結合，比如 `if (a < b) && (b < c)`。

將成功和失敗的概念與異常的概念相對比是很重要的；異常是不尋常的狀況，不是預期的結果。在 Icon 中失敗是預期的結果；到達檔案的結束處是預期的狀況而不是異常。Icon 沒有傳統意義上的例外處理，儘管失敗經常被用於類似異常的狀況下。例如，如果要讀取的檔案的不存在，`read` 失敗而不指示出特殊狀況。在傳統語言中，沒有指示這些「其他狀況」的自然方式，典型的例外處理是「丟擲」一個值，下面是用 Java 處理缺失檔案的例子：

這種情況需要兩個比較：一個用於檔案結束（EOF）而另一個用於所有其他錯誤。因為 Java 不允許異常作為邏輯元素來比較，就像 Icon 中那樣，轉而必須使用冗長的 `try/catch` 語法。`try` 塊即使沒有異常丟擲，也強加了效能上的懲罰，Icon 避免了這種分攤成本。

目的導向執行的一個關鍵方面，是程式可能必須在一個過程失敗時倒轉到以前的狀態，這個任務叫做回溯。例如，考慮設定一個變數為一個開始位置，並接著進行可以改變這個值的操作，這是在字串掃描中常見情況，這裡前進游標通過它所掃描的字串。如果這個過程失敗了，任何對這個變數的後續讀取都返回最初的狀態，而非被內部操縱後的狀態是很重要的。對於這種任務，Icon 有一個「可逆賦值」算子 `<-`，和「可逆交換」算子 `<->`。例如，考慮如下嘗試在一個更大字串內找到一個模式字串的代碼：

這個代碼開始於移動 `i` 到 `10`，這是尋找的開始位置。但是，如果 `find` 失敗，這個塊將作為整體失敗，作為一個不想要的副作用，它導致 `i` 的值留下為 `10`。故而應將 `i := 10` 替代為 `i <- 10`，指示 `i` 在這個塊失敗時應當被重設為它以前的值。這提供了執行中的原子性的類似者。

## 生成器

在 Icon 中表達式經常返回一個單一的值，例如 `5 > x`，將求值並且如果 `x` 的值小於 `5` 則成功並返回 `x`，否則失敗。但是，Icon 還包括了過程不立即返回成功或失敗，轉而每次呼叫它們之時返回一個新值的概念。這些過程叫做生成器，並且是 Icon 語言的關鍵部份。在 Icon 的用語中，一個表達式或函式的求值產生一個「結果序列」。結果序列包含這個表達式或函式生成的所有可能的值。在結果序列被耗盡的時候，這個表達式或函式失敗。

Icon 允許任何過程返回一個單一值或多個值，使用 `fail`、`return` 和 `suspend` 關鍵字來控制。缺乏任何這種關鍵字的過程返回 `&fail`，它在執行進行到一個過程的 `end` 處的時候發生。例如：

呼叫 `f(5)` 將返回 `1`，而呼叫 `f(-1)` 將返回 `&fail`。這將導致不明顯的行為，比如 `write(f(-1))` 將什麼都輸出，因為 `f` 失敗而暫停了 `write` 的操作。

將一個過程轉換成一個生成器，要使用 `suspend` 關鍵字，它意味著「返回這個值，並且在再次呼叫時，從這一點開始執行」。例如：

建立一個生成器，它返回一系列的數，開始於 `i` 並結束於 `j`，接著在它們之後返回 `&fail`。`suspend i` 停止執行，並返回 `i` 的值，而不重設任何狀態。當對相同函式做出另一次呼叫的時候，執行在這一點上拾起以前的值。在這種情況下，導致它進行 `i +:= 1`，迴圈回到 `while` 的開始處，並接著返回下一個值並再次暫停。這將持續直到 `i <= j` 失敗，在這一點上它登出這個塊並呼叫 `fail`。這允許輕易的構造迭代器。

另一種類型的生成器建造器是「交替子」（alternator），它的感觀和運算就像布林算子 `or`，例如：

這看起來是在說「如果 `y` 小於 `x` 或者 `5` 那麼...」，實際上它是生成器的一種簡寫形式，它返回值直到脫離於這個列表的結束處。這個列表的值被注入到運算之中，在這裡是 `<`。所以這個例子，系統首先測試 `y < x`，如果 `x` 實際上大於 `y`，它返回 `x` 的值，這個測試通過，而 `y` 的值在 `then` 子句中寫出。然而，如果 `x` 不大於 `y`，它失敗了，交替子繼續，進行 `y < 5`。如果這個測試通過，寫出 `y`。如果 `y` 不小於 `x` 或者 `5`，交替子用完了，測試失敗，`if` 子句失敗，而不進行 `write`。因此，`y` 的值如果小於 `x` 或 `5`，則它將出現在控制台上，從而履行了布林 `or` 的作用。函式不會被呼叫，除非求值它們的參數成功，所以這個例子可以簡寫為：

在內部，交替子不是簡單的一個 `or`，它還可以用來構造值的任意列表。這可以用來在任意的一組值上迭代，比如：

`every` 類似於 `while`，迴圈經過一個生成器的返回的所有專案，在失敗時登出。

因為整數列表在很多編程場景都是很常見的，Icon 還包括了 `to` 關鍵字來構造「事實上的」整數生成器：

在這種情況下，從 `i` 到 `j` 的值，將注入到 `write` 並寫出多行輸出。它可以簡寫為：

Icon 不是強型別的，所以交替子列表可以包含不同類型的專案：

這將依賴於 `x` 的值，寫出 `1`、`"hello"` 或可能的 `5`。

同樣的「合取算符」`&`，以類似於布林算符 `and` 的方式來使用：

這個代碼呼叫 `ItoJ` 並返回一個初始值 `0`，它被賦值給 `x`。接著進行合取的右手端，並且因為 `x % 2` 不等於 `0`，它寫出這個值。接著再次呼叫 `ItoJ` 生成器，它賦值 `1` 到 `x`，這使得右手端失敗而不列印任何東西。最終結果是從 `0` 到 `10` 的所有偶數的一個列表。

生成器的概念對於字串操作是很強大的。在 Icon 中，`find` 函式是個生成器。下面的例子代碼，在一個字串中找出 `"the"` 的所有出現位置：

`find` 在每次被 `every` 恢復的時候，將返回 `"the"` 的下一個實例的索引，最終達到字串結束處並失敗。

當然人們有時會想要找到在輸入中某點之後的一個字串，例如，掃描包含多列資料的一個文字檔案。目的導向執行也能起效：

只返回 `"the"` 出現在位置 `5` 之後的那些位置；否則比較會失敗。成功的比較返回右手側的結果，所以把 `find` 放置到這個比較的右手側是重要的。

## 搜集

Icon 包括了一些搜集類型，包括列表，它還可以用作堆疊和佇列，表格（在其他語言中也叫做對映或字典），和集合等。Icon 稱它們為「結構」。搜集是原生的生成器，並可以使用「嘆號語法」來輕易呼叫。例如：

使用如前面例子中見到的失敗傳播，可以組合測試和迴圈：

由於列表搜集是個生成器，可以使使用嘆號語法進一步簡化：

在這種情況下，在 `write` 內的嘆號，導致 Icon 從陣列一個接一個的返回一行文字，並且在結束處失敗。`&input` 是基於生成器的 `read` 的類似者，它從標準輸入讀取一行，所以 `!&input` 繼續讀取行直到檔案結束。

因為 Icon 是無類型的，列表可以包含任何不同類型的值：

在列表內的專案可以包括其他結構。為了建造更大的列表，Icon 包括了 `list` 生成器；`i := list(10, "word")` 生成包含 `"wold"` 的 `10` 個複本的一個列表。

就像其他語言中的陣列，Icon 允許專案按位元置來尋找，比如 `weight := aCat[4]`。就像陣列分片那樣，索引是在元素之間的，可以通過指定範圍來獲得列表的分片，比如 `aCat[2:4]` 產生列表 `["tabby",2002]`。

表格本質上是具有任意索引鍵而非僅為整數的列表：

這個代碼建立使用的 `0` 作為任何未知鍵的預設值的一個 `table`。接著向它增加了兩個專案，具有鍵 `"there"` 和 `"here"`，和分別的值 `1` 和 `2`。

集合也類似於列表，但是只包含任何給定值的一個單一成員。Icon 包括了 `++` 來產生兩個集合的併集，`**` 用於交集，和 `--` 用於差集。Icon 包括一些預定義的 `Cset`，即包含各種字元的集合。在 Icon 中有四個標準 `Cset`：`&ucase`、`&lcase`、`&letters` 和 `&digits`。可以通過用單引號包圍字串來建造 `Cset`，例如 `vowel := 'aeiou'`.

## 字串

在 Icon 中，字串是字元的列表。作為一個列表，它們是生成器，並可以使用「嘆號語法」來迭代：

這將在獨立行上列印出字串的每個字元。

子字串可以使用在方括號內的一個範圍規定從字串中提取出來。範圍規定可以返回到一個單一字元的一個點，或字串的一個分片（slice）。字串可以從左或從右索引。在一個字串內的位置被定義為在字元之間：`1A2B3C4`，也可以從右規定：`−3A−2B−1C0`。例如：

這裡最後例子採用了 `x1[i1+:i2] : x2` 表達式，產生 `x1` 在 `i1` 和 `i1 + i2` 之間的子字串。

子字串規定可以用作字串內的左值。這可以被用來把字串插入到另一個字串，或刪除字串的某部份。例如：

Icon 的下標索引是在元素之間的。給定字串 `s := "ABCDEFG"`，索引是 `1A2B3C4D5E6F7G8`。分片 `s[3:5]` 是在索引 `3` 和 `5` 之間的字串，它是字串 `"CD"`。

## 字串掃描

對處理字串的進一步簡化是「掃描」系統，通過 `?` 來發起，它在一個字串上呼叫函式：

Icon 稱呼 `?` 的左手端為「主語」，並將它傳遞到字串函式中。所呼叫的 `find` 接受兩個參數，尋找的文字作為參數一，而要在其中尋找的字串是參數二。使用 `?`，第二個參數是隱含的，而不由編程者來指定。在多個函式被依次呼叫在一個單一字串上的常見情況下，這種風格可以顯著的所見結果代碼的長度並增加清晰性。

`?` 不是簡單的一種語法糖，它還為任何隨後的字串操作，建立一個「字串掃描環境」。這基於了兩個內部變數，`&subject` 和 `&pos`，這裡的 `&subject` 是要掃描的字串，而 `&pos` 是在這個主語字串內的「游標」或當前位置。例如：

將產生：

內建和使用者定義的函式，可以被用於在要掃描的字串上移動。所有內建函式預設採用 `&subject` 和 `&pos`，來允許用上掃描語法。比如函式 `tab (i) : s`，它設定掃描位置：產生 `&subject[&pos:i]`，並將 `i` 賦值到 `&pos`。下列例子代碼，寫出在一個字串內，所有空白界定出的 `word`：

這個例子介入了一些新函式。`pos` 返回 `&pos` 的當前值。為何需要這個函式，而不簡單的直接使用 `&pos` 的值，不是顯而易見的；原因是 `&pos` 是一個變數，而不能呈現值 `&fail`，而過程 `pos` 能。因此 `pos` 提供對 `&pos` 的輕量級包裝，它允許輕易使用 Icon 的目的導向控制流，而不用針對 `&pos` 提供手寫的布林測試。在這種情況下，測試是「`&pos` 是零」，在 Icon 的字串位置的特異編碼中，零是行結束。如果它不是零，`pos` 返回 `&fail`，它通過 `not` 反轉而使得迴圈繼續。

`many` 從當前 `&pos` 開始，找到提供的 `Cset` 參數的一個或多個例子。在這種情況下，它尋找空格字元，所以這個函式的結果是在 `&pos` 之後的第一個非空格字元的位置。`tab` 移動 `&pos` 到那個位置，這種情況下再次具有潛在的 `&fail`，例如 `many` 在字元結束處脫離。`upto` 本質上是 `many` 的反函式；它返回緊前於提供的 `Cset` 的例子的位置，接著由另一個 `tab` 來設定 `&pos`。這裡的交替用來在行結束處也停止。

這個例子通過使用更合適的「字分隔」`Cset`，可以包括句號、逗號和其他標點，還有其他空白字元如 tab 和不換行空格，能夠變得更加健壯。這個 `Cset` 可以接著用於 `many` 和 `upto`。

一個更複雜的例子演示了在這個語言內生成器和字串掃描的整合：

表達式 `\*x` 計算 `x` 的大小。這裡介入了內建函式 `match (s1,s2,i1,i2) : i3`，它匹配初始字串：如果 `s1 == s2[i1+:\*s1]`，產生 `i1 + \*s1`，否則失敗；它設定有預設值：`s2` 為 `&subject`，如果 `s2` 預設了，`i1` 為 `&pos`，否則為 `1`，`i2` 為 `0`。

## 參見

* 協程

## 註解

## 參照

## 參考書目

* Griswold, Ralph; Griswold, Madge. The Icon Programming Language (third edition). Peer-to-Peer Communications. 2002 [2020-09-19]. ISBN1-57398-001-3. （原始內容存檔於 2020-11-09）.
* Griswold, Ralph; Griswold, Madge. History of the Icon Programming Language. Communications of the ACM. March 1993, **23** (3): 53–68.
* Griswold, Ralph. A History of the SNOBOL Programming Languages. Wexelblat, Richard (編). History of Programming Languages. Academic Press. 1981.
* Griswold, Ralph. An Overview of the Icon Programming Language; Version 9. Department of Computer Science, The University of Arizona. 2 March 1996 [2022-02-03]. （原始內容存檔於 2022-04-08）.
* Tratt, Laurence. Experiences with an Icon-like expression evaluation system (PDF). Proceedings of the 6th symposium on Dynamic Languages. 18 October 2010: 73–80 [2022-02-03]. doi:10.1145/1869631.1869640. （原始內容 (PDF) 存檔於 2021-11-04）.

## 外部連結

* Icon homepage（頁面存檔備份，存於網際網路檔案館）
* Oral history interview with Stephen Wampler, Charles Babbage Institute, University of Minnesota. Wampler discusses his work on the development Icon in the late 1970s.
* Oral history interview with Robert Goldberg, Charles Babbage Institute, University of Minnesota. Goldberg discusses his interaction with Griswold when working on Icon in the classroom at Illinois Institute of Technology.
* Oral history interview with Kenneth Walker, Charles Babbage Institute, University of Minnesota. Walker describes the work environment of the Icon project, his interactions with Griswold, and his own work on an Icon compiler.
* The Icon Programming Language page （頁面存檔備份，存於網際網路檔案館） on The Rosetta Code comparative programming tasks project site