第二章 基礎(chǔ)知識(shí)
2.1 編譯和運(yùn)行Icon程序
作者假設(shè)你的系統(tǒng)上安裝了Icon 9版本。
假設(shè)你把Icon程序命名為“test”。你把你的Icon程序放在一個(gè)文件“test.icn”中。然后用icont命令編譯它
? ? icont test
如果編譯沒有出現(xiàn)錯(cuò)誤,則使用命令運(yùn)行它
? ? test
如果有錯(cuò)誤,Icon編譯器會(huì)告訴你它在哪里遇到了錯(cuò)誤以及錯(cuò)誤是什么。我們將在稍后的第14章中進(jìn)行更全面的討論。
2.2 Hello, world
習(xí)慣上,寫一個(gè)“hello, world”程序作為開始,正常的話它會(huì)輸出“hello, world”來。
例1 Hello world
? ? procedure main()
? ? ? ? write("hello, world")
? ? end
您可以看到編寫過程的方法:以procedure開始,以end結(jié)束。過程后面是過程名稱和參數(shù)列表,可以為空。
write函數(shù)將其參數(shù)寫入輸出,然后終止該行(再用一條write會(huì)從下一行開始寫)。
字符串是用雙引號(hào)括起來。
當(dāng)程序開始運(yùn)行時(shí),它執(zhí)行名為main的過程,就像在C中一樣。
在下面的例2中,我們展示了在Icon中,用新行或分號(hào)(如果您愿意,也可以兩者都用)分隔序列中的表達(dá)式。Icon按順序執(zhí)行表達(dá)式。該writes函數(shù)(注意相比上例中函數(shù)多個(gè)s)將其參數(shù)寫入輸出,但不終止該行。接下來寫的內(nèi)容將在同一行。
例2 表達(dá)式序列
? ? procedure main()
? ? ? ? writes(”hello,”)
? ? ? ? write(” world”)
? ? end
或
? ? procedure main()
? ? ? ? writes(”hello,”);
? ? ? ? write(” world”)
? ? end
或
? ? procedure main()
? ? ? ? writes(”hello,”); write(” world”)
? ? end
Icon是一種弱類型語言。這意味著變量沒有被聲明為具有特定的數(shù)據(jù)類型。只有值才有數(shù)據(jù)類型,任何類型的值都可以賦值給任何變量。就此而言,變量根本不需要聲明。下面例3說明了這一點(diǎn)。
賦值操作符是“:=”。
變量x被分配了兩個(gè)不同類型的值,第一個(gè)是字符串,然后是整數(shù)。
過程write可以像輸出字符串一樣輸出整數(shù)。事實(shí)上,它會(huì)輸出任何它知道如何轉(zhuǎn)換為字符串的東西。
例3 弱類型
? ? procedure main()
? ? ? ? x := "Example "
? ? ? ? writes(x)
? ? ? ? x := 1
? ? ? ? write(x)
? ? end
例4顯示了局部聲明和交換操作符。
過程中的局部變量在進(jìn)入過程時(shí)分配內(nèi)存,在過程返回時(shí)消失。如果沒有對(duì)變量的聲明,如例3中的x,則編譯器將其變?yōu)榫植孔兞俊?/p>
“:=:”是交換操作符;它將交換兩個(gè)變量的值。
例4 局部聲明
? ? procedure main()
? ? ? ? local x,y
? ? ? ? x := " Example "
? ? ? ? y := 2
? ? ? ? write(x,y)
? ? ? ? x :=: y
? ? ? ? write(x,y)
? ? end
當(dāng)創(chuàng)建變量時(shí),它們被賦予初始值null,這導(dǎo)致大多數(shù)操作在運(yùn)行時(shí)報(bào)告錯(cuò)誤。您將經(jīng)常遇到這種運(yùn)行時(shí)錯(cuò)誤。
例5 未初始化變量
? ? procedure main()
? ? ? ? local x
? ? ? ? write(x+1)#this will cause an error at run time
? ? ? ? ? ? ? ? ? # (and notice: comments begin with
? ? ? ? ? ? ? ? ? # and run to the end of the line)
? ? end
2.3 聲明
我們已經(jīng)看到了局部聲明。Icon實(shí)際上提供了以下所有種類的聲明:
local x,y,z? #定義在過程內(nèi)。每當(dāng)進(jìn)入過程時(shí),創(chuàng)建變量的新副本。在過程返回時(shí)刪除它們。這些名稱僅在過程中是已知的。對(duì)于未聲明的變量,假設(shè)是局部的,但不要使用此特性:稍后引入全局聲明可能會(huì)導(dǎo)致過程停止工作。
static x,y,z? #定義在過程內(nèi)。在程序開始執(zhí)行時(shí)創(chuàng)建變量的副本。這些名稱僅在過程中是已知的。靜態(tài)變量只有一個(gè)副本。它在過程調(diào)用之間保持其值。
global x,y,z? #定義在過程外。在程序開始執(zhí)行時(shí)創(chuàng)建變量的副本。只有在沒有為局部變量或靜態(tài)變量聲明相同名稱的所有過程中才知道這些名稱。全局變量只有一個(gè)副本。
procedure? #定義在過程外,不能嵌套。參見第5章“過程”
name(x,y,z)
? ? ...
end
record name(x,y,z)? #定義在過程外。參加第2.10節(jié)和第12章
link name? #定義在過程外。告訴鏈接器該程序使用文件名中聲明的過程、記錄或全局變量。名稱可以是Icon標(biāo)識(shí)符,但如果包含Icon標(biāo)識(shí)符中不允許的字符,則必須是帶引號(hào)的字符串。
2.4 退出程序
有幾種方法可以退出Icon程序。您已經(jīng)看到的方法是從過程main返回。還有兩個(gè)函數(shù)也被使用,exit和stop。
exit()? #以正常退出狀態(tài)退出程序(即,告訴操作系統(tǒng)一切正常)。
exit(n)? #退出程序并返回整數(shù)n作為退出狀態(tài)。這是告訴操作系統(tǒng)事情不正常的方法,但是您必須知道操作系統(tǒng)如何解釋這些退出狀態(tài)值才能使用它。
stop(s1,s2,...,sn)? #輸出字符串s1 s2…Sn,退出時(shí)顯示錯(cuò)誤狀態(tài)。進(jìn)一步請(qǐng)看第8章I/O部分。
2.5? 數(shù)字
2.5.1 整數(shù)字面值
您可以將整數(shù)字面值(常量)寫成十進(jìn)制數(shù),例如25。
2.5.2 整數(shù)運(yùn)算符
+? 加號(hào)? 優(yōu)先級(jí)8
-? 減號(hào)? 優(yōu)先級(jí)8
*? 乘號(hào)? 優(yōu)先級(jí)9
/? 除號(hào)? 優(yōu)先級(jí)9
%? 余號(hào)? 優(yōu)先級(jí)9
^? 乘方? 優(yōu)先級(jí)10
除冪運(yùn)算符首先在最右邊執(zhí)行外,其他運(yùn)算符從左到右執(zhí)行。運(yùn)算符*、/和%在+和-之前完成。^運(yùn)算符在其他運(yùn)算符之前執(zhí)行。也就是說,優(yōu)先級(jí)數(shù)大的操作符在優(yōu)先級(jí)小的運(yùn)算符之前執(zhí)行。
2.6 字符串
2.6.1 字符串字面值
你寫一個(gè)用引號(hào)括起來的字符串字面值(常量):
"Like this"
如果您需要在字符串中包含引號(hào),請(qǐng)?jiān)谄淝懊婕由戏葱备?,例?\""。如果你需要包含一個(gè)反斜杠,在它前面放一個(gè)反斜杠,"\\"。有一些特殊的方法可以包含其他字符,見后面的章節(jié)。
2.6.2 字符串操作符
您可以使用“||”運(yùn)算符連接兩個(gè)字符串,例如:
? ? s:="ab"
? ? s:=s||"cd"
? ? write(s)
會(huì)輸出“abcd”。
您可以使用一元“*”運(yùn)算符找出字符串的長度,例如:
? ? s:="abc"
? ? write(*s)
? ? s:="a"
? ? write(*s)
? ? s:=""
? ? write(*s)
會(huì)輸出
? ? 3
? ? 1
? ? 0
2.6.3 字符串下標(biāo)
字符串中的字符按照字符串長度從1開始編號(hào)。你可以給字符串下標(biāo),就像在大多數(shù)其他語言中給數(shù)組下標(biāo)一樣,把索引放在字符串后面的括號(hào)里:
? ? s:="find"
? ? write(s[3])
? ? s[4] := "e"
? ? write(s)
會(huì)輸出
? ? n
? ? fine
與C語言不同,它沒有單獨(dú)的字符類型。只有長度為1的字符串。上面的表達(dá)式[3]返回一個(gè)長度為1的字符串“n”。
當(dāng)用字符串下標(biāo)賦值,可以賦值多個(gè)字符的字符串或空字符串。例如:
? ? s:="fund"
? ? s[4] := ""
? ? write(s)
? ? s[3] := "nny"
? ? write(s)
會(huì)輸出
? ? fun
? ? funny
Icon還允許您使用位置范圍下標(biāo)字符串,選擇多于或少于一個(gè)字符。
s[i:j]
(其中i≤j),它從字符i到字符j之間選擇子字符串,但不包括字符j。
如果分配給子字符串,則替換所選字符。如果在賦值時(shí)i=j,則插入到字符i之前。如果i=j=*s+1,則追加到字符串s之后
? ? s:="12345"
? ? write(s)
? ? s[3:3]:="x"? #當(dāng)i=j時(shí),會(huì)把字符串插到原字串位置i字符之前
? ? write(s)
會(huì)輸出
? ? 12345
? ? 12x345
? ? s:="12345"
? ? write(s)
? ? s[3:4]:="x"? #當(dāng)i:i+1時(shí),會(huì)把字符串插到原字串位置i字符那
? ? write(s)
會(huì)輸出
? ? 12345
? ? 12x45
? ? s:="12345"
? ? write(s)
? ? s[3:5]:="x"? #當(dāng)i:i+n(2或更大)時(shí),會(huì)把原字串位置i到i+(n-1)的字符串變"",然后插入此位置
? ? write(s)
會(huì)輸出
? ? 12345
? ? 12x5
? ? s:="12345"
? ? write(s)
? ? s[3:5]:="xyz"? #當(dāng)i:i+n(2或更大)時(shí),會(huì)把原字串位置i到i+(n-1)的字符串變"",然后插入此位置
? ? write(s)
會(huì)輸出
? ? 12345
? ? 12xyz5
? ? s:="12345"
? ? write(s)
? ? s[*s+1:*s+1]:="yz"? #i=j=*s+1,則追加到字符串s之后
? ? write(s)
會(huì)輸出
? ? 12345
? ? 12345yz
? ? s := "12345"
? ? write(s[6])? #超出長度不會(huì)報(bào)錯(cuò),只會(huì)輸出空串
會(huì)輸出
s := "12345"
write(s[0])? #取下標(biāo)0會(huì)輸出空串
會(huì)輸出
? ? s := "12345"
? ? write(s[-1])? #當(dāng)負(fù)數(shù)下標(biāo)會(huì)從右側(cè)數(shù)取位置
會(huì)輸出
? ? 5
2.6.4 比較運(yùn)算符
下面是數(shù)字和字符串的基本比較運(yùn)算符:
i=j? s1==s2? 相等? 優(yōu)先級(jí)6
i~=j? s1~==s2? 不相等? 優(yōu)先級(jí)6
i<j? s1<<s2? 小于? 優(yōu)先級(jí)6
i<=j? s1<<=s2? 小于等于? 優(yōu)先級(jí)6
i>j? s1>>s2? 大于? 優(yōu)先級(jí)6
i>=j? s1>>=s2? 大于等于? 優(yōu)先級(jí)6
2.7 基本控制結(jié)構(gòu)
以下是Icon中使用的三種最常見的控件結(jié)構(gòu)。我們省略了大部分細(xì)節(jié),詳見第4章:
2.7.1 If表達(dá)式
你可以使用if表達(dá)式選擇要執(zhí)行的代碼:
? ? if expr1 then expr2 else expr3
目前,我們只在expr1中使用一個(gè)比較運(yùn)算符。如果expr1成功(在其他語言中我們會(huì)說,如果expr1為真,但在Icon中我們不會(huì)這么說,我們?cè)谶@兒說“成功”),那么Icon執(zhí)行expr2,否則,Icon執(zhí)行expr3。
第3章和第4章將更詳細(xì)地討論expr1的其他選項(xiàng)。
因?yàn)閕f表達(dá)式是一個(gè)表達(dá)式,所以它返回一個(gè)值,expr2或expr3的值。
2.7.2 While表達(dá)式
你可以使用while表達(dá)式來重復(fù)執(zhí)行一些代碼:
? ? while expr1 do expr2
同樣,目前我們將把自己限制在expr1中的單個(gè)關(guān)系操作符。只要expr1成功,Icon就執(zhí)行expr2。盡管while表達(dá)式是一個(gè)表達(dá)式,但它不返回值。
2.7.3 表達(dá)式序列
你可以使用大括號(hào){expr1;expr2;…;exprn},對(duì)表達(dá)式序列進(jìn)行分組,以便將它們包含在if表達(dá)式或while表達(dá)式中。大括號(hào)內(nèi)的表達(dá)式用分號(hào)分隔,或者用新行分隔,或者兩者同時(shí)分隔,就像過程體中的表達(dá)式序列一樣。
表達(dá)式序列是一個(gè)表達(dá)式。它返回序列中最后一個(gè)表達(dá)式的值。
2.8 基本生成器
生成器是傳遞一系列值的表達(dá)式,是Icon的核心。它們將在第3章中深入討論。這里我們只展示其中一個(gè)用途。
這循環(huán)
? ? every i := 1 to 10 do expr
是Icon中的for循環(huán)。表達(dá)式“1 to 10”是一個(gè)生成器,它生成整數(shù)1,2,…10。每個(gè)值被賦給變量i,表達(dá)式expr被求值。
幾個(gè)生成器可以與&操作符組合使用,以實(shí)現(xiàn)嵌套循環(huán)的效果:
? ? every i := 1 to 10 & j := 1 to 10 do expr
行為類似于兩個(gè)嵌套的for循環(huán)。當(dāng)i = 1時(shí),j將從1迭代到10,然后當(dāng)i = 2時(shí),j將從1迭代到10,以此類推。
您還可以添加測試以消除一些迭代:
? ? every i := 1 to 10 & j := 1 to 10 & i ~= j do expr
如果I和j的值相同就會(huì)忽略expr的取值。
2.9 基本列表
2.9.1 List創(chuàng)建:list (n)
列表類似于其他語言中的數(shù)組。您可以使用list函數(shù)創(chuàng)建一個(gè)元素編號(hào)為1、2、…n的列表
例如:
? ? L:=list(3)
將創(chuàng)建一個(gè)長度為3的列表,并將其賦值給變量L。列表中的所有元素將初始化為null。
2.9.2 列表下標(biāo)
長度為n的列表是一個(gè)包含n個(gè)元素的數(shù)組,元素的編號(hào)從1到n,就像數(shù)組一樣。列表下標(biāo)的方式與字符串相同:將下標(biāo)表達(dá)式放在列表后面的括號(hào)中,例如:
? ? L:=list(2)
? ? L[1]:=5
? ? L[2]:=10
? ? write(L[1])
會(huì)輸出
? ? 5
您可以創(chuàng)建長度為0的列表。只需list(0)
2.9.3 List創(chuàng)建:[...]
如果要?jiǎng)?chuàng)建一個(gè)包含特定值的短列表,你可以在括號(hào)中列出你想要的值:
? ? L:=[5,10]
? ? write(L[1])
會(huì)輸出
? ? 5
您可以通過寫[]來創(chuàng)建一個(gè)長度為0的列表。
2.9.4 List創(chuàng)建:list(n,x)
如果你想創(chuàng)建一個(gè)所有元素都相同的列表,但不是null,使用list(n,x),它將創(chuàng)建一個(gè)長度為n的列表,所有元素都是x。
2.9.5 列表操作符
你可以用操作符“|||”連接兩個(gè)列表,例如:
? ? s:=[5,6]
? ? s:=s|||[7,8]
? ? write(s[3])
會(huì)輸出
? ? 7
x|||y的結(jié)果是一個(gè)新的列表,其中包含x的元素和y的元素。列表x和y不會(huì)改變。
你可以使用一元*運(yùn)算符找出列表的長度,例如:
? ? s:=[1,2,3]
? ? write(*s)
? ? s:=[]
? ? write(*s)
會(huì)輸出
? ? 3
? ? 0
可以使用===或~===操作符比較兩個(gè)列表是否相同。(連續(xù)使用三個(gè)等號(hào)。)怎樣才能成為相同的列表呢?請(qǐng)看
? ? L:=[1,2]
? ? M:=L
在此代碼之后,L === M將成功,L ~=== M將失敗。賦值M:=L使L和M指向同一個(gè)列表。再看
? ? L:=[1,2]
? ? M:=[1,2]
在此代碼之后,L ~=== M將成功,L === M將失敗。賦值M:=[1,2]使得M指向一個(gè)不可能與L相同的新列表。即使L和M指向具有相同長度和相同內(nèi)容的列表,它們也不相同。
例子:
? ? x:=1
? ? y:=1
? ? write(if x === y then "equal" else "not equal")
會(huì)輸出
? ? equal
例子:
? ? x:=1
? ? y:="1"
? ? write(if x === y then "equal" else "not equal")
會(huì)輸出
? ? not equal
例子:
? ? L:=[[1],[1]]
? ? write(if L[1] === L[2] then "equal" else "not equal")
會(huì)輸出
? ? not equal
因?yàn)閮纱螁为?dú)出現(xiàn)的[1]創(chuàng)建了兩個(gè)不同的列表。
例子:
? ? L:=list(2,[1])
? ? write(if L[1] === L[2] then "equal" else "not equal")
會(huì)輸出
? ? equal
因?yàn)樵趌ist函數(shù)中出現(xiàn)[1]只計(jì)算一次,創(chuàng)建一個(gè)列表,它被分配給L的兩個(gè)元素。
2.9.6 列表與字符串的區(qū)別
下面是列表和字符串之間的一些區(qū)別:
字符串有字面值,列表沒有字面值。
列表是可變值;字符串是不可變的。這意味著您可以更改列表中的一個(gè)元素,并在與該元素相等(===)的所有列表中看到該更改。如果您更改字符串變量中的一個(gè)字符,Icon實(shí)際上會(huì)用所做的更改創(chuàng)建一個(gè)新字符串,并將該新字符串賦值給變量。
當(dāng)您將值賦給列表的元素時(shí),列表的長度不會(huì)改變。將字符串賦值給下標(biāo)字符串時(shí),字符串的長度可以更改。您分配的字符串在替換該位置的字符時(shí)被拼接。
過程write將輸出一個(gè)字符串。它不會(huì)寫出一個(gè)列表。
可以將一個(gè)列表賦值給另一個(gè)列表的元素。你可以將一個(gè)列表賦值給它自身的元素,得到一個(gè)循環(huán)結(jié)構(gòu)。
雖然Icon里皆是表達(dá)式,但不能對(duì)一個(gè)引號(hào)的字符串用下標(biāo)方式改變內(nèi)容,如:
? ? "abcd"[2]:="x"? #這是錯(cuò)的
但神奇的是,Icon列表這么寫卻是合法的,如:
? ? [1,2,3][2]:=5
2.9.7 main過程參數(shù)
過程main接受一個(gè)參數(shù),是個(gè)列表變量,所有命令行傳來的參數(shù)都放入這個(gè)字符串列表。如上所述,實(shí)際上不必用參數(shù)聲明過程main。下面是一個(gè)使用參數(shù)的例子,一個(gè)程序回顯命令行參數(shù):
? ? procedure main(args)
? ? ? ? i := 1
? ? ? ? while i <= *args do {
? ? ? ? write(args[i])
? ? ? ? i:=i+1
? ? ? ? }
? ? end
執(zhí)行test -a 123 -b xyz
會(huì)顯示
? ? -a
? ? 123
? ? -b
? ? xyz
2.10 記錄
您可以在Icon中創(chuàng)建新的記錄數(shù)據(jù)類型,就像在Pascal(記錄)、C(結(jié)構(gòu))和c++(類)中一樣。聲明一個(gè)記錄類型:
? ? record rname(f1,f2,...,fn)
? “rname”是給記錄類型的名稱。
? f1, f2, …, fn是給定給記錄的字段(成員)的名稱。
? 記錄聲明只允許在過程聲明之外使用。
例子
? ? record Point(x,y)
可用于在二維坐標(biāo)系中定義一個(gè)點(diǎn)。
一個(gè)點(diǎn)可以通過表達(dá)式創(chuàng)建:
? ? r := Point(1,2)
它將創(chuàng)建一個(gè)Point類型的新記錄,將其x字段初始化為1,y字段初始化為2,并將Point記錄賦值給變量r。
記錄的字段可以使用二進(jìn)制“?!保侄我?,操作符,例如。
記錄的字段可以使用“.”操作符來訪問,例如:
? ? r.x := r.y
與 Pascal、C 和 C++ 不同,Icon沒有顯式指針。
? ? p1 := Point(1,2)
? ? p2 := p1 #p2 points to p1
? ? p2.x := 2 #also changes p1.x
? ? write(if p1 === p2 then "equal" else "not equal")
? ? write(if p1.x === p2.x then "equal"
? ? else "not equal")
會(huì)輸出
? ? equal
? ? equal