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

Icon
編程範型多範式:面向文字, 結構化
設計者Ralph Griswold英語Ralph Griswold
釋出時間1977年,​47年前​(1977
目前版本
  • 951(2013年6月5日;穩定版本)[1]
編輯維基數據連結
型態系統動態
許可證公有領域
網站www.cs.arizona.edu/icon
主要實作產品
Icon, Jcon
衍生副語言
Unicon
啟發語言
SNOBOL[2], SL5[3], ALGOL
影響語言
Unicon, Python[4], Goaldi[5], jq

歷史

編輯

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

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

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

  • SNOBOL4的哲學和語意基礎;
  • SL5的語法基礎;
  • SL5的特徵,排除廣義的過程機制。

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

基本語法

編輯

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

Icon還在很多方面分享了多數手稿語言(還有SNOBOL及SL5)的特徵:變數不需要聲明,類型是自動轉換的,就說數字和字串可以自動來迴轉換。另一個常見於很多而非全部的手稿語言的特徵,是指令碼中缺少行終止字元;在Icon中,對於不結束於分號的行,由Icon編譯器自動的插入分號來終結,而這種插入的前提是它不會導致跨行的表達式遭到錯誤的分隔。

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

procedure main() 
    write("Hello, world!")
end

Icon允許任何過程,執行returnsuspend陳述式,來分別返回一個單一值或依次返回多個值中的一個值。過程已經執行到它的end處,或者執行了fail陳述式,它會返回&fail。例如:

procedure fn(x)
    if x > 0 then {
        return 1
    }
    fail
end

這裏的fail在不是必需的,因為它緊前於end,增加它是為了清晰性。呼叫fn(5)將返回1,而呼叫fn(-1)將返回&fail。這將導致不明顯的行為,比如write(fn(-1))由於fn(-1)失敗而導致write()也跟着中止了;還有x := fn(-1)x賦值也不會發生[10]

目標導向執行

編輯

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

* 打印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而繼續下去[11]

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

while a := read() then write(a)

它的含義是:「只要讀取不返回失敗,呼叫寫出,否則停止」[13]。在Icon中,read()函數返回一行文字或&fail&fail不同於C語言中的特殊返回值EOF(檔案結束),它被語言依據上下文明確理解為意味着「停止處理」或「按失敗狀況處理」。這裏即使read()導致一個錯誤它都會工作,比如說如果檔案不存在。在這種情況下,陳述式a := read()會失敗,而write()簡單的不被呼叫。

沿呼叫鏈傳遞

編輯

成功和失敗將沿着呼叫鏈向上載遞,意味着可以將函數呼叫嵌入其他函數呼叫內,在巢狀的函數呼叫失敗時,它們整體停止。例如,上面的代碼可以精簡為[14]

while write(read())

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

成敗測試

編輯

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

write(a < b)

另一個不同是<算子如果成功,返回它的第二個運算元,在這個例子中,如果b大於a,則導致b的值被寫出,否則什麼都不寫。因為並非測試本身,而是一個算子返回一個值,它們可以串聯在一起,允許如下這樣的鏈式測試[14]

a < b < c

在多數語言中的平常類型的比較之下,同樣的語意必須寫為兩個不等式合取,比如在C語言中表達為a < b && b < c

需要注意如果測試一個變數比如c,意圖確定它是否已初始化,它在未初始化時返回一個值&null,所以對它的測試總是成功的,故而需要測試c === &nullc ~=== &null[10]

回溯

編輯

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

例如下列表達式:

i := 10 & j := i < find(pattern, inString)

其中的find()在指定字串中尋找符合特定模式的子字串並依次返回其起始位置。這個表達式由其優先級比賦值:=更低的&分隔為兩部份,在第一部份中設置了一個閾值i10。在第二個部份中,如果find()能成功返回了一個值並且這個值大於閾值,則i < find(pattern, inString)成功,接着將這個值賦值到j;否則失敗,對j的賦值和整個表達式都會一起失敗。作為在失敗情況下不想要的副作用,這個表達式的第一部份會導致i的值遺留為10,故而應將i := 10替代為i <- 10

i <- 10 & j := i < find(pattern, inString)

在這個表達式失敗之時,i會被重設為它以前的值。這提供了在執行中的類似於原子性英語Atomic commit的機制。

例外

編輯

將成功和失敗的概念與例外的概念相對比是很重要的:異常是不尋常的狀況,不是預期的結果;而例外是預期的結果,比如讀取檔案時到達檔案的結束處,是預期的例外狀況而不是異常。Icon沒有傳統意義上的例外處理,儘管失敗經常被用於類似例外的狀況下。例如,如果要讀取的檔案的不存在,read()失敗而不指示出特殊狀況[13]。在傳統語言中,沒有指示這些「其他狀況」的自然方式,典型的例外處理是throw「投擲」一個值。例如Java使用try/catch語法來處理例外:

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)

遍歷生成器所生成的所有結果,可以使用表達式every expr1 do expr2,這裏的do子句是可選的,它針對expr1生成的每個結果求值expr2,在expr1不再產生結果時失敗。

中綴表達式to的優先級高於賦值算子。在這種情況下,從ij的值,將注入到write()並寫出多行輸出[10]。它可以簡寫為:

every write(i to j)

在Icon中,經常將合取算子&用於控制流程,它的使用方式類似於C語言和Bourne Shell短路求值邏輯與算子&&,需要注意不同於C語言的情況,&的優先級低於賦值算子:=[15]

every x := 0 to 10 & x % 2 == 0 do write(x)

這段代碼呼叫整數生成器並得到其初始值0,它被賦值給x。接着進行合取的右手端,因為x % 2確實等於0而成功,進而合取x := 0 to 10 & x % 2 == 0成功,隨即執行write()寫出x的值。接着再次呼叫這個生成器,它賦值1x,這使得合取的右手端失敗進而合取失敗,故而不寫出任何東西。最終結果是寫出從010的一個列表中的所有偶數[15]

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

s := "All the world's a stage. And all the men and women merely players"
every write(find("the", s))

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

在想要在一個字串的某點之後的進行尋找之時,目標導向執行也能起效:

every write(5 < find("the", s))

如果找出的"the"出現在位置5之後,則比較成功並返回右手側的結果,否則比較失敗進行下一次尋找。這裏把find()放置到這個比較的右手側是重要的。

交替表達式

編輯

最常見類型的生成器建造器是|交替英語Alternation (formal language theory)(alternation)表達式,求值交替者的嚴格語法描述為:expr1 | expr2 : x1, x2, ...expr1 | expr2首先生成expr1的結果,然後生成expr2的結果,最終依次產生多個值x1, x2, ...。它可以用來構造值的任意列表。例如可以在任意的一組值上進行迭代:

every i := 1|3|4|5|10|11|23 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)

暫停表達式

編輯

要將一個過程轉換成一個生成器,可以使用表達式suspend expr1 do expr2,這裏的do子句是可選的;它暫停當前過程,將expr1生成的每個結果作為這個過程所產生結果返回。在再次呼叫這個過程之時於這個暫停之處恢復執行,此時先求值expr2再恢復expr1;如果expr1是生成器並產生了新結果,則再次暫停並返回它的結果;如果expr1不是生成器或者是生成器但不再產生新結果,則繼續執行後面的陳述式。

例如:自己定義一個ItoJ生成器[13]

procedure ItoJ(i, j)
    while i <= j do {
        suspend i
        i +:= 1
    }
end

它建立一個生成器,它返回一系列的數,開始於i並結束於j,接着在它們之後返回&failsuspend i停止執行,並返回i的值,而不重設這個函數的任何狀態。當對相同函數做出另一次呼叫的時候,在這一點上於以前的狀態下恢復執行。它接着進行i +:= 1,然後迴圈回到while的開始處,接着返回下一個值並再次暫停。這個迴圈將持續直到i <= j失敗,這時呼叫fail退出。這種機制允許輕易的構造迭代器[13]

將例子稍作修改:

procedure ItoJ(i, j)
    while i <= j do {
        suspend i | i*i
        i +:= 1
    }
end
procedure main ()
    every write(ItoJ(1,2))
end

將產生:

1
1
2
4

數據結構

編輯

Icon將值的搜集稱為「結構」,包括:記錄列表集合英語Set (abstract data type)表格

記錄是固定大小的並且它們的值通過欄位名來提及。記錄像過程那樣聲明並且對整個程式而言是全域的,一個記錄的實例可以通過記錄構造子來建立。例如:

record employee(name, age, ssn, salary)
clerk := employee("John Doe", 36, "123–45–6789", 35000.0)

記錄的欄位通過名字.字段形式的表達式來提及,例如clerk.salary

列表在Icon有兩個角色。它是可用下標定位的一維陣列向量),也是可用專屬訪問函數操縱從而增長或縮短的堆疊佇列。因為Icon是無類型的,列表可以包含任何不同類型的值:

clerk := ["John Doe", 36, "123–45–6789", 35000.0]

就像其他語言中的陣列,Icon允許專案按編號從1開始的位置來尋找,例如salary := clerk[4]。在Icon中,可以通過指定範圍來獲得列表的分節(section),就像陣列分片英語Array slicing那樣,索引設立在元素之間,比如clerk[2:4]產生列表[36, "123–45–6789"]。在列表內的專案可以包括其他結構。Icon包括了list()列表建立函數,用來建造更大的列表;例如vector := list(10, 0.0),生成100.0的一個列表。

集合英語Set (abstract data type)類似於列表,但是只包含任何給定值的一個單一成員。Icon包括了++來產生兩個集合的併集,**用於交集,和--用於差集。可以通過用單引號包圍字串來建造Cset,例如:

vowel := 'aeiou'

Icon包括一些預定義的Cset,即包含各種字元的集合,它有四個標準Cset&ucase&lcase&letters&digits

表格是有序對的搜集,有序對被稱為元素,它由鍵和對應的值組成。表格在其他語言中叫做關聯陣列、對映或字典。表格類似於列表,但是它的鍵或稱為「下標」不必須為整數而可以是任何類型的值:

symbols := table(0)
symbols["here"] := 1
symbols["there"] := 2

表格建立函數table()建立了使用的0作為任何未知鍵的預設值的一個表格。接着向它增加了兩個專案,具有鍵"here""there",及其各自的值12

搜集遍歷

編輯

搜集可以通過原生的生成器來遍歷。例如針對檔案的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,其嚴格語法描述為!x1 : x2, x3, ..., xn,從一個運算元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一個接一個的返回一行文字,並且在結束處失敗。而!&input,類似於從標準輸入檔案&input讀取一行的read(),一個接一個讀取並返回諸行直到檔案結束。

在Icon中,字串字元列表,可以使用「嘆號語法」來迭代:

every write(!"Hello, world!")

這將一行一個的列印出字串的每個字元。

子字串提取

編輯

字串字元列表,可以使用在方括號內的一個範圍規定從字串中提取出子字串。子字串範圍規定,可以返回到一個單一字元的一個點,或字串的一個分節(section)或分片英語array slicing(slice)。

字串可以從左至右索引或者從右至左索引,並且在一個字串內的位置被定義為在字元之間:1A2B3C4或者−3A−2B−1C0。例如:

"Wikipedia"[1] == "W"
"Wikipedia"[3] == "k"
"Wikipedia"[-1] == "a"
"Wikipedia"[1:3] == "Wi"
"Wikipedia"[-2:0] == "ia"
"Wikipedia"[2+:3] == "iki"

"Wikipedia"[0]會導致失敗。這裏最後例子採用了x1[i1+:i2] : x2表達式,產生x1i1i1 + i2之間的子字串。

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

s := "abc"
s[2] := "123"     # s现在的值是"a123c"
s := "abcdefg" 
s[3:5] := "ABCD"  # s现在的值是"abABCDefg"
s := "abcdefg"
s[3:5] := ""      # s现在的值是"abefg"

字串掃描

編輯

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

s ? write(find("the"))

Icon稱呼?的左手端為「主語」,並將它傳遞到字串函數中。所呼叫的生成器函數find()接受兩個參數,尋找的文字和要在其中進行尋找的字串。使用?之時,第二個參數是隱含的,而不再由編程者來指定。多個函數被依次呼叫於一個單一字串上,是一種常見情況,這種風格可以顯著的縮減代碼長度並增加清晰性。

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

表達式x ? expr : x,儲存當前的主語和位置,並接着分別設置它們為x1,接着求值expr;它的產出就是expr的產出,在從expr退出時它將主語和位置復原為儲存的值。例如:

procedure main()
    local s
    s := "this is a string"
    s ? write("subject=[",&subject,"] pos=[",&pos,"]")
end

將產生:

subject=[this is a string] pos=[1]

內建和用戶定義的函數,可以被用於在要掃描的字串上移動。所有內建函數預設採用&subject&pos,來允許用上掃描語法。比如設置掃描位置的函數tab(i) : s:產生子字串&subject[&pos:i],並且將i賦值到&pos。下列例子代碼,寫出在一個字串內,所有空白界定出的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。這個例子介入了一些新函數,pos(i1) : i2用於測試掃描位置,如果&pos = i1返回&pos,否則失敗。這裏的迴圈以通過not反轉的pos(0)作為條件,0在Icon的字串位置編碼中指示行結束,如果當前掃描位置不是0pos()返回&fail。Icon不簡單的直接使用&pos,因為&pos是一個變數,不能提供&fail值,需要提供函數pos()&pos做輕量級包裝,從而便於使用Icon的目標導向控制流程。

many()函數從當前&pos開始,找到在所提供的Cset參數中字元的最長序列之後的位置。在這例子中,它尋找空格字元,所以這個函數的結果是在&pos之後的第一個非空格字元的位置;接着用tab()移動&pos到那個位置。upto()函數返回在所提供的Cset中字元之前的位置,接着由另一個tab()來設置&pos

這個例子通過可以使用更合適的「字分隔」Cset,包括上句號、逗號和其他標點,還有其他空白字元如tab和不換行空格,從而變得更加健壯,還可以將其用於many()upto()函數。

下面給出結合了生成器與字串掃描的一個更複雜的例子:

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 := [
            "Mon","Tue","Wed","Thr","Fri","Sat","Sun"]
        months := [
            "Jan","Feb","Mar","Apr","May","Jun",
            "Jul","Aug","Sep","Oct","Nov","Dec"]
    }
    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

初始化子句形式為initial expr,其中的expr是於所在函數首次被呼叫時求值的表達式。表達式=s1等價於tab(match(s1))。表達式*x計算x的大小。這裏介入了內建函數match(s1,s2,i1,i2) : i3,它匹配初始字串:如果s1 == s2[i1+:*s1],產生i1 + *s1,否則失敗;它設定有預設值:s2&subjecti1s2預設時為&pos,否則為1i20

八皇后問題例子

編輯

採用回溯法求解八皇后問題的例子代碼:

procedure main() 
    local s, t, p
    s := "abcdefgh"
    every t := sort([
        s[q(1)]||"1", s[q(2)]||"2", 
        s[q(3)]||"3", s[q(4)]||"4",
        s[q(5)]||"5", s[q(6)]||"6",
        s[q(7)]||"7", s[q(8)]||"8"]) 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
abcdefgh
8
 
 
 
 
 
 
 
 
 
8
77
66
55
44
33
22
11
abcdefgh
解1
abcdefgh
8
 
 
 
 
 
 
 
 
 
8
77
66
55
44
33
22
11
abcdefgh
解2

可逆(reversible)賦值算子<-,在被恢復的時候,上次暫停時它所設置的值被逆轉回原來的值。不使用的<-話,可以將這裏的suspend col[c] <- …… <- c替代為suspend col[c] := …… := c do col[c] := …… := 0

將這段代碼儲存入queens.icn檔案中,下面演示其執行結果並提取其92個解中的前兩個解:

$ icon ./queens.icn | wc -l
92
$ icon ./queens.icn | sed -n '1,2p'
a1 b7 c5 d8 e2 f4 g6 h3
a1 b7 c4 d6 e8 f2 g5 h3

參見

編輯

參照

編輯
  1. ^ Release 951. 2013年6月5日 [2023年9月19日]. 
  2. ^ Griswold, Ralph E.; Poage, J.F.; Polonsky, Ivan P. The SNOBOL 4 Programming Language 2nd. Englewood Cliffs NJ: Prentice-Hall. 1971. ISBN 0-13-815373-6. 
  3. ^ Ralph E. Griswold, David R. Hanson, "An Overview of SL5", SIGPLAN Notices 12:4:40-50 (April 1977)
  4. ^ Guido van Rossum. Python Reference Manual - Version 1.2 (PDF). CWI Report CS-R9525. May 1995 [2023-03-04]. (原始內容存檔 (PDF)於2023-03-05). Python is a simple, yet powerful, interpreted programming language …… . Its syntax is put together from constructs borrowed from a variety of other languages; most prominent are influences from ABC, C, Modula-3 and Icon. 
  5. ^ Goaldi
  6. ^ Griswold, Ralph E.; Griswold, Madge T. History of the Icon programming language. Bergin, Thomas J.; Gibson, Richard G. (編). History of Programming Languages II. New York NY: ACM Press. 1996. 
  7. ^ Griswold 1981,第609頁.
  8. ^ Griswold 1981,第629頁.
  9. ^ 9.0 9.1 9.2 9.3 9.4 Griswold & Griswold 1993,第53頁.
  10. ^ 10.0 10.1 10.2 Tratt 2010,第75頁.
  11. ^ 11.0 11.1 Lane, Rupert. SNOBOL - Introduction. Try MTS. 26 July 2015 [2022-02-03]. (原始內容存檔於2022-05-09). 
  12. ^ Tratt 2010,第73頁.
  13. ^ 13.0 13.1 13.2 13.3 Tratt 2010,第74頁.
  14. ^ 14.0 14.1 Griswold 1996,第2.1頁.
  15. ^ 15.0 15.1 Tratt 2010,第76頁.

參考書目

編輯

外部連結

編輯