Icon 是一門領域特定的高階程式語言，有著 目標（goal）導向執行 特徵，和操縱字串和文字模式的很多設施。它衍生自 SNOBOL 和 SL5 字串處理語言。Icon 不是面向物件的，但在 1996 年開發了叫做 Idol 的面向物件擴充套件，它最終變成了 Unicon。 歷史 在 1971 年 8 月，SNOBOL 的設計者之一離開了貝爾實驗室，成為了亞利桑那大學的教授。他那時將 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 編譯器自動的插入分號來終結，而這種插入的前提是它不會導致跨行的表示式遭到錯誤的分隔。 過程是 Icon 程式的基本建造塊。儘管它們使用 Pascal 名稱，但工效上更像 C 函式並可以返回值；在 Icon 中沒有 function 關鍵字。 procedure main write Hello world end Icon 允許任何過程，執行或語句，來分別返回一個單一值或依次返回多個值中的一個值。過程已經執行到它的處，或者執行了語句，它會返回。例如： procedure fn x if x 0 then return 1 fail end 這裡的在不是必需的，因為它緊前於，增加它是為了清晰性。呼叫 fn 5 將返回 1，而呼叫 fn 1 將返回。這將導致不明顯的行為，比如 write fn 1 由於 fn 1 失敗而導致 write 也跟著中止了；還有 x fn 1 對 x 賦值也不會發生。 目標導向執行 Icon 的關鍵概念之一就是其控制結構基於表示式的 成功 或 失敗，而非大多數其他程式語言中的布林邏輯。這個特徵直接派生自 SNOBOL，在其中表達式求值、模式匹配和模式匹配連帶替換，都可以跟隨著成功或失敗子句，用來指定在這個條件下要分支到一個語句標籤。例如，下列程式碼列印 Hello World 五次： 列印 Hello World 五次的 SNOBOL 程式 I 1 LOOP OUTPUT Hello World I I 1 LE I 5 S LOOP END 要進行迴圈，在索引變數 I 之上呼叫內建的函式 LE （小於等於），並且 S LOOP 測試它是否成功，即在 I 小於等於 5 之時，分支到命名標籤 LOOP 而繼續下去。 Icon 保留了基於成功或失敗的控制流程的基本概念，但進一步發展了語言。一個變更是將加標籤的 GOTO 式的分支，替代為面向塊的結構，符合在 1960 年代後期席捲計算機工業的結構化程式設計風格。另一個變更是允許失敗沿著呼叫鏈向上傳遞，使得整個塊作為一個整體的成功或失敗。這是 Icon 語言的關鍵概念。而在傳統語言中，必須包括基於布林邏輯的測試成功或失敗的程式碼，並接著基於產出結果進行分支，這種測試和分支是固有於 Icon 程式碼的，而不需要明確的寫出。考慮如下複製標準輸入到標準輸出的簡單程式碼： while a read then write a 它的含義是：只要讀取不返回失敗，呼叫寫出，否則停止。在 Icon 中，read 函式返回一行文字或。不同於 C 語言中的特殊返回值 EOF（檔案結束），它被語言依據上下文明確理解為意味著 停止處理 或 按失敗狀況處理。這裡即使 read 導致一個錯誤它都會工作，比如說如果檔案不存在。在這種情況下，語句 a read 會失敗，而 write 簡單的不被呼叫。 沿呼叫鏈傳遞 成功和失敗將沿著呼叫鏈向上傳遞，意味著可以將函式呼叫嵌入其他函式呼叫內，在巢狀的函式呼叫失敗時，它們整體停止。例如，上面的程式碼可以精簡為： while write read 在 read 命令失敗的時候，比如在檔案結束之處，失敗將沿著呼叫鏈上傳，而 write 也會失敗。while 作為一個控制結構，在失敗時停止。Icon 稱謂這個概念為 目標導向執行，指稱這種只要某個目標達到執行就繼續的方式。在上面的例子中目標是讀整個檔案；讀命令在有資訊讀到的時候成功，而在沒有的時候失敗。目標因此直接編碼於語言中，不用再去檢查返回碼或類似的構造。 成敗測試 Icon 使用目標導向機制用於進行傳統的布林測試，儘管有著微妙的差異。一個簡單的比較如，這裡的 if 子句，不像在多數語言中那樣意味著：如果測試運算求值為真；轉而它的意味更像是：如果測試運算成功。在這個例子情況下，如果這個比較為真，則 運算成功。如果 if 子句測試這個表示式成功，則呼叫 then 子句，如果它失敗了，則呼叫 else 子句或下一行。結果同於在其他語言中見到的傳統 if then ，它表示如果 a 小於 b 為真，則執行 then 關聯的語句。微妙之處是相同的比較表示式可以放置在任何地方，例如： write a b 另一個不同是 運算元如果成功，返回它的第二個運算元，在這個例子中，如果 b 大於 a，則導致 b 的值被寫出，否則什麼都不寫。因為並非測試本身，而是一個運算元返回一個值，它們可以串聯在一起，允許如下這樣的鏈式測試： a b c 在多數語言中的平常型別的比較之下，同樣的語義必須寫為兩個不等式合取，比如在 C 語言中表達為。 需要注意如果測試一個變數比如 c，意圖確定它是否已初始化，它在未初始化時返回一個值，所以對它的測試總是成功的，故而需要測試或。 回溯 目標導向執行的一個關鍵方面，是程式可能必須在一個過程失敗時倒轉到以前的狀態，這種任務叫做回溯。例如，考慮設定一個變數為一個開始位置，並接著進行可以改變這個值的操作，這是在字串掃描中常見情況，這裡前進遊標透過它所掃描的字串。如果這個過程失敗了，任何對這個變數的後續讀取都返回最初的狀態，而非被內部操縱後的狀態是很重要的。對於這種任務，Icon 有一個可逆（reversible）賦值運算元 ，和可逆交換運算元 。 例如下列表達式： i 10 j i find pattern inString 其中的在指定字串中查詢符合特定模式的子字串並依次返回其起始位置。這個表示式由其優先順序比賦值 更低的 分隔為兩部份，在第一部份中設定了一個閾值 i 為 10。在第二個部份中，如果能成功返回了一個值並且這個值大於閾值，則成功，接著將這個值賦值到 j；否則失敗，對 j 的賦值和整個表示式都會一起失敗。作為在失敗情況下不想要的副作用，這個表示式的第一部份會導致 i 的值遺留為 10，故而應將 i 10 替代為 i 10： i 10 j i find pattern inString 在這個表示式失敗之時，i 會被重置為它以前的值。這提供了在執行中的類似於的機制。 例外 將成功和失敗的概念與例外的概念相對比是很重要的：異常是不尋常的狀況，不是預期的結果；而例外是預期的結果，比如讀取檔案時到達檔案的結束處，是預期的例外狀況而不是異常。Icon 沒有傳統意義上的例外處理，儘管失敗經常被用於類似例外的狀況下。例如，如果要讀取的檔案的不存在，read 失敗而不指示出特殊狀況。在傳統語言中，沒有指示這些 其他狀況 的自然方式，典型的例外處理是 throw 丟擲 一個值。例如 Java 使用 trycatch 語法來處理例外： try 讀取檔案等等。 catch EOFException e 遇到檔案結束例外 catch IOException e 發生 IO 異常 生成器 在 Icon 中表達式經常返回一個單一的值，例如 5 x，將求值並且如果 x 的值小於 5 則成功並返回 x，否則失敗。但是，Icon 還包括了過程不立即返回成功或失敗，轉而每次呼叫它們之時返回一個新值的概念。這些過程叫做生成器，並且是 Icon 語言的關鍵部份。在 Icon 的用語中，一個表示式或函式的求值產生一個 結果序列。結果序列包含這個表示式或函式生成的所有可能的值。在結果序列被耗盡的時候，這個表示式或函式失敗。 因為整數列表在很多程式設計場景都是很常見的，Icon 包括了 to 中綴表示式來構造整數生成器： every k i to j do write k 遍歷生成器所生成的所有結果，可以使用表示式，這裡的 do 子句是可選的，它針對 expr1 生成的每個結果求值 expr2，在 expr1 不再產生結果時失敗。 中綴表示式 to 的優先順序高於賦值運算元。在這種情況下，從 i 到 j 的值，將注入到 write 並寫出多行輸出。它可以簡寫為： every write i to j 在 Icon 中，經常將合取運算元 用於控制流程，它的使用方式類似於 C 語言和 Bourne Shell 中短路求值的邏輯與運算元 ，需要注意不同於 C 語言的情況， 的優先順序低於賦值運算元： every x 0 to 10 x 2 0 do write x 這段程式碼呼叫整數生成器並得到其初始值 0，它被賦值給 x。接著進行合取的右手端，因為 x 2 確實等於 0 而成功，進而合取成功，隨即執行 write 寫出 x 的值。接著再次呼叫這個生成器，它賦值 1 到 x，這使得合取的右手端失敗進而合取失敗，故而不寫出任何東西。最終結果是寫出從 0 到 10 的一個列表中的所有偶數。 生成器的概念對於字串操作是很強大的。在 Icon 中，函式是個生成器。下面的例子程式碼，在一個字串中找出 the 的所有出現位置： s All the worlds a stage And all the men and women merely players every write find the s 在每次被 every 恢復的時候，將返回 the 的下一個例項的索引，最終達到字串結束處並失敗。 在想要在一個字串的某點之後的進行查詢之時，目標導向執行也能起效： every write 5 find the s 如果找出的 the 出現在位置 5 之後，則比較成功並返回右手側的結果，否則比較失敗進行下一次查詢。這裡把放置到這個比較的右手側是重要的。 交替表示式 最常見型別的生成器建造器是 即（alternation）表示式，求值交替者的嚴格語法描述為：，首先生成 expr1 的結果，然後生成 expr2 的結果，最終依次產生多個值 x1 x2 。它可以用來構造值的任意列表。例如可以在任意的一組值上進行迭代： every i 1345101123 do write i Icon 不是強型別的，所以交替表示式形成的列表可以包含不同型別的專案： every i 1 hello x 5 do write i 這將寫出 1、hello 和在 x 5 的情況下出現的 5。 交替表示式的優先順序低於比較運算元，高於 to 表示式和賦值運算元。它在條件語句中擔當 C 語言和 Bourne Shell 中短路求值的邏輯或運算元 功能，例如： if y x 5 then write y y 這段程式碼中的 符號起到了邏輯或的作用：如果 y 小於 x 或者 5，那麼寫出 y 的值。實際上 x 5 是生成器的一種簡寫形式，它依次返回這個列表的值直到脫離於這個列表結束處，而這個列表的值被注入到這裡的 運算之中。首先測試 y x，如果 x 實際上大於 y，則這次測試成功並且 if 子句成功；否則這次測試失敗而交替運算繼續，從而再次測試 y 5。如果直到交替運算完成而所有測試都未透過，則 if 子句失敗。在 if 子句成功之後，才會執行 then 子句 write 寫出 y 的值。 函式只有在求值它們的引數成功之後才會被呼叫，所以這個例子可以簡寫為： write y x 5 y 暫停表達式 要將一個過程轉換成一個生成器，可以使用表示式，這裡的 do 子句是可選的；它暫停當前過程，將 expr1 生成的每個結果作為這個過程所產生結果返回。在再次呼叫這個過程之時於這個暫停之處恢復執行，此時先求值 expr2 再恢復 expr1；如果 expr1 是生成器併產生了新結果，則再次暫停並返回它的結果；如果 expr1 不是生成器或者是生成器但不再產生新結果，則繼續執行後面的語句。 例如：自己定義一個 ItoJ 生成器： procedure ItoJ i j while i j do suspend i i 1 end 它建立一個生成器，它返回一系列的數，開始於 i 並結束於 j，接著在它們之後返回。停止執行，並返回 i 的值，而不重置這個函式的任何狀態。當對相同函式做出另一次呼叫的時候，在這一點上於以前的狀態下恢復執行。它接著進行 i 1，然後迴圈回到 while 的開始處，接著返回下一個值並再次暫停。這個迴圈將持續直到 i j 失敗，這時呼叫退出。這種機制允許輕易的構造迭代器。 將例子稍作修改： procedure ItoJ i j while i j do suspend i ii i 1 end procedure main every write ItoJ 12 end 將產生： 1 1 2 4 資料結構 Icon 將值的蒐集稱為 結構，包括：記錄、列表、和表格。 記錄是固定大小的並且它們的值透過欄位名來提及。記錄像過程那樣宣告並且對整個程式而言是全域性的，一個記錄的例項可以透過記錄構造子來建立。例如： record employee name age ssn salary clerk employee John Doe 36 123456789 350000 記錄的欄位透過名字。欄位形式的表示式來提及，例如。 列表在 Icon 有兩個角色。它是可用下標定位的一維陣列（向量），也是可用專屬訪問函式操縱從而增長或縮短的堆疊和佇列。因為 Icon 是無型別的，列表可以包含任何不同型別的值： clerk John Doe 36 123456789 350000 就像其他語言中的陣列，Icon 允許專案按編號從 1 開始的位置來查詢，例如。在 Icon 中，可以透過指定範圍來獲得列表的分節（section），就像那樣，索引設立在元素之間，比如產生列表。在列表內的專案可以包括其他結構。Icon 包括了列表建立函式，用來建造更大的列表；例如，生成 10 個 00 的一個列表。 類似於列表，但是隻包含任何給定值的一個單一成員。Icon 包括了 來產生兩個集合的並集， 用於交集，和 用於差集。可以透過用單引號包圍字串來建造 Cset，例如： vowel aeiou Icon 包括一些預定義的 Cset，即包含各種字元的集合，它有四個標準 Cset：、、和。 表格是有序對的蒐集，有序對被稱為元素，它由鍵和對應的值組成。表格在其他語言中叫做關聯陣列、對映或字典。表格類似於列表，但是它的鍵或稱為 下標 不必須為整數而可以是任何型別的值： symbols table 0 symbols here 1 symbols there 2 表格建立函式建立了使用的 0 作為任何未知鍵的預設值的一個表格。接著向它增加了兩個專案，具有鍵 here 和 there，及其各自的值 1 和 2。 蒐集遍歷 蒐集可以透過固有的生成器來遍歷。例如針對檔案的 read 、針對佇列的 get 和針對堆疊的 pop ： lines 建立一個空列表 while line read do 迴圈從標準輸入讀取行 put lines line 將行新增到佇列 while line get lines do 迴圈從佇列取出行 write line 將行寫到標準輸出 使用如前面例子中見到的失敗傳播，可以組合測試和迴圈： lines 建立空列表 while put lines read 新增直到所讀檔案為空 while write get lines 寫出直到所取佇列為空 字首運算元，其嚴格語法描述為，從一個運算元 x1 生成多個值 x2 x3 xn： 如果 x1 是一個檔案，x1 生成 x1 餘下的諸行。 如果 x1 是一個字串，x1 生成 x1 的一個字元的諸子串，並且如果 x1 是變數則產生諸變數。 如果 x1 是一個列表、表格或記錄，x1 生成具有 x1 諸元素的諸變數。產生次序，對於列表和記錄是從開始至結束，而對於表格是不可預測的。 如果 x1 是一個集合，x1 以不可預測的次序生成 x1 的諸成員。 遍歷蒐集可以使使用歎號語法進一步簡化： lines every put lines input every write lines 在這種情況下，在 write 內的！lines，導致從列表 lines 一個接一個的返回一行文字，並且在結束處失敗。而，類似於從標準輸入檔案讀取一行的 read ，一個接一個讀取並返回諸行直到檔案結束。 在 Icon 中，字串是字元的列表，可以使用 歎號語法 來迭代： every write Hello world 這將一行一個的打印出字串的每個字元。 子字串提取 字串是字元的列表，可以使用在方括號內的一個範圍規定從字串中提取出子字串。子字串範圍規定，可以返回到一個單一字元的一個點，或字串的一個分節（section）或（slice）。 字串可以從左至右索引或者從右至左索引，並且在一個字串內的位置被定義為在字元之間：1A2B3C4 或者3A2B1C0。例如： Wikipedia1 W Wikipedia3 k Wikipedia1 a Wikipedia13 Wi Wikipedia20 ia Wikipedia23 iki 會導致失敗。這裡最後例子採用了表示式，產生 x1 在 i1 和 i1 i2 之間的子字串。 子字串規定可以用作字串內的左值。這可以被用來把字串插入到另一個字串，或刪除字串的某部份。例如： s abc s 2 123 s 現在的值是 a123c s abcdefg s 35 ABCD s 現在的值是 abABCDefg s abcdefg s 35 s 現在的值是abefg 字串掃描 對處理字串的進一步簡化是 掃描 系統，透過？來發起，它在一個字串上呼叫函式： s write find the Icon 稱呼？的左手端為 主語，並將它傳遞到字串函式中。所呼叫的生成器函式接受兩個引數，查詢的文字和要在其中進行查詢的字串。使用？之時，第二個引數是隱含的，而不再由程式設計者來指定。多個函式被依次調用於一個單一字串上，是一種常見情況，這種風格可以顯著的縮減程式碼長度並增加清晰性。 不是簡單的一種語法糖，它還為任何隨後的字串操作，建立一個 字串掃描環境。這基於了兩個內部變數，和，這裡的是要掃描的字串，而是在這個主語字串內的當前位置或 遊標。 表示式，儲存當前的主語和位置，並接著分別設定它們為 x 和 1，接著求值 expr；它的產出就是 expr 的產出，在從 expr 退出時它將主語和位置復原為儲存的值。例如： procedure main local s s this is a string s write subjectsubject pospos end 將產生： subjectthis is a string pos1 內建和使用者定義的函式，可以被用於在要掃描的字串上移動。所有內建函式預設採用和，來允許用上掃描語法。比如設定掃描位置的函式：產生子字串，並且將 i 賦值到。下列例子程式碼，寫出在一個字串內，所有空白界定出的 word： procedure main local s t words s this is a string words s 建立字串掃描環境 while not pos 0 do 測試字串結束 tab many 躍過任何空白 put words tab upto 0 新增直到下一個空白或結束之前的諸字元 t get words every t words write t end 將產生： this is a string 運算元 串接兩個字串， 是一種增廣賦值，a b 等價於 a a b。這個例子介入了一些新函式，用於測試掃描位置，如果返回，否則失敗。這裡的迴圈以透過 not 反轉的作為條件，0 在 Icon 的字串位置編碼中指示行結束，如果當前掃描位置不是 0，返回。Icon 不簡單的直接使用，因為是一個變數，不能提供值，需要提供函式對做輕量級包裝，從而便於使用 Icon 的目標導向控制流程。 函式從當前開始，找到在所提供的 Cset 引數中字元的最長序列之後的位置。在這例子中，它查詢空格字元，所以這個函式的結果是在之後的第一個非空格字元的位置；接著用移動到那個位置。函式返回在所提供的 Cset 中字元之前的位置，接著由另一個來設定。 這個例子透過可以使用更合適的 字分隔 Cset，包括上句號、逗號和其他標點，還有其他空白字元如 tab 和不換行空格，從而變得更加健壯，還可以將其用於和函式。 下面給出結合了生成器與字串掃描的一個更復雜的例子： procedure main local s s Mon Dec 8 s write Mdate not a valid date end 定義一個匹配函式，它返回匹配 星期 月份 日期 的一個字串 procedure Mdate local retval static months days initial 定義一些初始值 days MonTueWedThrFriSatSun months JanFebMarAprMayJun JulAugSepOctNovDec suspend retval tab match days 儲存匹配星期的一個字串 追加隨後匹配的一個空白 tab match months 追加隨後匹配月份的一個字串 追加隨後匹配的一個空白 matchdigits 2 追加隨後匹配的最多 2 位數字 pos 0 隨後匹配一個空白或者結束 retval 返回儲存的字串 end 返回最多 n 位數字的一個字串的匹配函式 procedure matchdigits n local v suspend v tab many digits v n v end 將產生： Mon Dec 8 初始化子句形式為，其中的 expr 是於所在函式首次被呼叫時求值的表示式。表示式等價於。表示式計算 x 的大小。這裡介入了內建函式，它匹配初始字串：如果，產生，否則失敗；它設定有預設值：s2 為；i1 在 s2 預設時為，否則為 1；i2 為 0。 八皇后問題例子 採用回溯法求解八皇后問題的例子程式碼： procedure main local s t p s abcdefgh every t sort s q 11 s q 22 s q 33 s q 44 s q 55 s q 66 s q 77 s q 88 do p get t every p t write p end procedure q r suspend place r 1 to 8 end procedure place r c static col up down initial col list 8 0 down list 15 0 up list 15 0 if col c down r c 1 up r c 8 0 then suspend col c down r c 1 up r c 8 c end 可逆（reversible）賦值運算元 ，在被恢復的時候，上次暫停時它所設定的值被逆轉回原來的值。不使用的 話，可以將這裡的替代為。 將這段程式碼儲存入 queensicn 檔案中，下面演示其執行結果並提取其 92 個解中的前兩個解： icon queensicn wc l 92 icon queensicn sed n 12p a1 b7 c5 d8 e2 f4 g6 h3 a1 b7 c4 d6 e8 f2 g5 h3 參見 協程 引用 參考書目 外部連結 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 Category 動態型別程式語言 Category 模式匹配程式語言 Category 公有領域軟體 Category 面向文字程式語言 Category1977 年建立的程式語言