【Icon (编程语言)】
Icon是一门领域特定的高级编程语言，有着“目的（goal）导向执行”特征，和操纵字符串和文本模式的很多设施。它衍生自SNOBOL和SL5字符串处理语言。Icon不是面向对象的，但在1996年开发了叫做Idol的面向对象扩展，它最终变成了Unicon。
==历史==
在1971年8月，SNOBOL的设计者之一Ralph Griswold离开了贝尔实验室，成为了亚利桑那大学的教授pp=609。他那时将SNOBOL4介入为研究工具pp=629。
作为最初在1960年代早期开发的语言，SNOBOL的语法带有其他早期编程语言的印记，比如FORTRAN和COBOL。特别是，语言是依赖列的，像很多要录入到打孔卡的语言一样，有着列布局是很自然的。此外，控制结构几乎完全基于了分支，而非使用块，而块在ALGOL 60中介入之后，已经成为了必备的特征。在他迁移到亚利桑那的时候，SNOBOL4的语法已然过时了p=53。
Griswold开始致力于用传统的流程控制结构如if…then，来实现SNOBOL底层的成功和失败概念。这成为了SL5，即“SNOBOL Language 5”的简写，但是结果不令人满意p=53。在1977年，他考虑设计语言的新版本。他放弃了在SL5中介入的非常强力的函数系统，介入更简单的暂停和恢复概念，并为SNOBOL4自然后继者开发了新概念，具有如下的原则p=53：
* SNOBOL4的哲学和语义基础；
* SL5的语法基础；
* SL5的特征，排除广义的过程机制。
新语言最初叫做SNOBOL5，但因为除了底层概念外，全都与SNOBOL有着显著的差异，最终想要一个新名字。在这个时候Xerox PARC出版了他们关于图形用户界面的工作，术语“icon”从而进入了计算机词汇中。起初确定为“icon”而最终选择了“Icon”p=53。
==基本语法==
Icon语言衍生自ALGOL类的结构化编程语言，因而有着类似C或Pascal的语法。Icon最类似于Pascal的，是使用了:=语法的赋值，procedure关键字和类似的语法。在另一方面，Icon使用C风格的花括号来结构化执行分组，并且程序开始于运行叫做main的过程。
Icon还在很多方面分享了多数脚本语言（还有SNOBOL及SL5）的特征：变量不需要声明，类型是自动转换的，就说数字和字符串可以自动来回转换。另一个常见于很多而非全部的脚本语言的特征是，缺少行终止字符；在Icon中，不结束于分号的行，若其确有意义则由暗含的分号来终结。
过程是Icon程序的基本建造块。尽管它们使用Pascal名称，但工效上更像C函数并可以返回值；在Icon中没有function关键字。
procedure doSomething(aString)
write(aString)
end
==目的导向执行==
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（成功），意味着I小于等于5，它分支到命名标签LOOP并继续下去。
Icon保留了基于成功或失败的控制流程的基本概念，但进一步发展了语言。一个变更是将加标签的GOTO式的分支，替代为面向块的结构，符合在1960年代后期席卷计算机工业的结构化编程风格p=53。另一个变更是允许失败沿着调用链向上传递，使得整个块作为一个整体的成功或失败。这是Icon语言的关键概念。而在传统语言中，必须包括基于布尔逻辑的测试成功或失败的代码，并接着基于产出结果进行分支，这种测试和分支是固有于Icon代码的，而不需要明确的写出p=73。考虑如下复制标准输入到标准输出的简单代码：
while a := read() then write(a)
它的含义是：“只要读取不返回失败，调用写出，否则停止”p=74。在Icon中，read()函数返回一行文本或&fail。&fail不是简单的Java中的特殊返回值EOF（文件结束）的类似者，因为它被语言依据上下文明确理解为意味着“停止处理”或“按失败状况处理”。这里即使read()导致一个错误它都会工作，比如说如果文件不存在。在这种情况下，语句a := read()会失败，而写操作简单的不调用。
成功和失败将沿着调用链向上传递，意味着可以将函数嵌入其他函数内，在nested function失败时，它们整体停止。例如，上面的代码可以精简为p=2.1：
while write(read())
在read()命令失败的时候，比如在文件结束之处，失败将沿着调用链上传，而write()也会失败。while作为一个控制结构，在失败时停止。Icon称谓这个概念为“目的导向执行”，指称这种只要某个目的达到执行就继续的方式。在上面的例子中目的是读整个文件；读命令在有信息读到的时候成功，而在没有的时候失败。目的因此直接编码于语言中，不用再去检查返回码或类似的构造。
Icon使用目的导向机制用于进行传统的布尔测试，尽管有着微妙的差异。一个简单的比较如code|if a if子句，不像在多数语言中那样意味着：“如果右侧运算求值为真”；转而它的意味更像是：“如果右侧运算成功”。在这种情况下，如果这个比较为真，算子成功。如果if子句的这个表达式成功，则调用then子句，如果它失败了，则调用else子句或下一行。结果同于在其他语言中见到的传统if…then，如果a小于b，if进行then子句。微妙之处是相同的比较表达式可以放置在任何地方，例如：
write(a
另一个不同是算子如果成功，返回它的第二个实际参数，在这个例子中，如果b大于a，则导致它的值被写出，否则什么都不写。因为并非测试本身，而是一个算符返回一个值，它们可以串联在一起，允许像if a 这样的事情p=2.1 ，在多数语言中平常类型的比较下，必须写为两个不等式的结合，比如if (a 。
将成功和失败的概念与异常的概念相对比是很重要的；异常是不寻常的状况，不是预期的结果。在Icon中失败是预期的结果；到达文件的结束处是预期的状况而不是异常。Icon没有传统意义上的异常处理，尽管失败经常被用于类似异常的状况下。例如，如果要读取的文件的不存在，read失败而不指示出特殊状况p=74。在传统语言中，没有指示这些“其他状况”的自然方式，典型的异常处理是“抛出”一个值，下面是用Java处理缺失文件的例子：
try {
while ((a = read()) != EOF) {
write(a);
}
} catch (Exception e) {
// 某个事情出错了，使用这个catch来退出循环
}
这种情况需要两个比较：一个用于文件结束（EOF）而另一个用于所有其他错误。因为Java不允许异常作为逻辑元素来比较，就像Icon中那样，转而必须使用冗长的try/catch语法。try块即使没有异常抛出，也强加了性能上的惩罚，Icon避免了这种Distributed cost。
目的导向执行的一个关键方面，是程序可能必须在一个过程失败时倒转到以前的状态，这个任务叫做回溯。例如，考虑设置一个变量为一个开始位置，并接着进行可以改变这个值的操作，这是在字符串扫描中常见情况，这里前进游标通过它所扫描的字符串。如果这个过程失败了，任何对这个变量的后续读取都返回最初的状态，而非被内部操纵后的状态是很重要的。对于这种任务，Icon有一个“可逆赋值”算子，和“可逆交换”算子。例如，考虑如下尝试在一个更大字符串内找到一个模式字符串的代码：
{
(i := 10) &
(j := (i
这个代码开始于移动i到10，这是查找的开始位置。但是，如果find失败，这个块将作为整体失败，作为一个不想要的副作用，它导致i的值留下为10。故而应将i := 10替代为i ，指示i在这个块失败时应当被重置为它以前的值。这提供了执行中的原子性的类似者。
==生成器==
在Icon中表达式经常返回一个单一的值，例如5 > x，将求值并且如果x的值小于5则成功并返回x，否则失败。但是，Icon还包括了过程不立即返回成功或失败，转而每次调用它们之时返回一个新值的概念。这些过程叫做生成器，并且是Icon语言的关键部份。在Icon的用语中，一个表达式或函数的求值产生一个“结果序列”。结果序列包含这个表达式或函数生成的所有可能的值。在结果序列被耗尽的时候，这个表达式或函数失败。
Icon允许任何过程返回一个单一值或多个值，使用fail、return和suspend关键字来控制。缺乏任何这种关键字的过程返回&fail，它在执行进行到一个过程的end处的时候发生。例如：
procedure f(x)
if x > 0 then {
return 1
}
end
调用f(5)将返回1，而调用f(-1)将返回&fail。这将导致不明显的行为，比如write(f(-1))将什么都输出，因为f失败而暂停了write的操作p=75。
将一个过程转换成一个生成器，要使用suspend关键字，它意味着“返回这个值，并且在再次调用时，从这一点开始执行”。例如p=74：
procedure ItoJ(i, j)
while i
建立一个生成器，它返回一系列的数，开始于i并结束于j，接着在它们之后返回&fail。fail在这种情况下是不要求的，因为它紧前于end，增加它是为了清晰性。suspend i停止执行，并返回i的值，而不重置任何状态。当对相同函数做出另一次调用的时候，执行在这一点上拾起以前的值。在这种情况下，导致它进行i +:= 1，循环回到while的开始处，并接着返回下一个值并再次暂停。这将持续直到i 失败，在这一点上它退出这个块并调用fail。这允许轻易的构造迭代器p=74。
另一种类型的生成器建造器是“交替子”（alternator），它的感观和运算就像布尔算子or，例如：
if y
这看起来是在说“如果y小于x或者5那么...”，实际上它是生成器的一种简写形式，它返回值直到脱离于这个列表的结束处。这个列表的值被注入到运算之中，在这里是。所以这个例子，系统首先测试y ，如果x实际上大于y，它返回x的值，这个测试通过，而y的值在then子句中写出。然而，如果x不大于y，它失败了，交替子继续，进行y 。如果这个测试通过，写出y。如果y不小于x或者5，交替子用完了，测试失败，if子句失败，而不进行write。因此，y的值如果小于x或5，则它将出现在控制台上，从而履行了布尔or的作用。函数不会被调用，除非求值它们的参数成功，所以这个例子可以简写为：
write("y=", (x | 5) > y)
在内部，交替子不是简单的一个or，它还可以用来构造值的任意列表。这可以用来在任意的一组值上迭代，比如：
every i := (1|3|4|5|10|11|23) do write(i)
every类似于while，循环经过一个生成器的返回的所有项目，在失败时退出p=75。
因为整数列表在很多编程场景都是很常见的，Icon还包括了to关键字来构造“事实上的”整数生成器：
every k := i to j do write(k)
在这种情况下，从i到j的值，将注入到write并写出多行输出p=75。它可以简写为：
every write(i to j)
Icon不是强类型的，所以交替子列表可以包含不同类型的项目：
every i := (1 | "hello" | x
这将依赖于x的值，写出1、"hello"或可能的5。
同样的“合取算符”&，以类似于布尔算符and的方式来使用p=76：
every x := ItoJ(0,10) & x % 2 == 0 do write(x)
这个代码调用ItoJ并返回一个初始值0，它被赋值给x。接着进行合取的右手端，并且因为x % 2不等于0，它写出这个值。接着再次调用ItoJ生成器，它赋值1到x，这使得右手端失败而不打印任何东西。最终结果是从0到10的所有偶数的一个列表p=76。
生成器的概念对于字符串操作是很强大的。在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"的下一个实例的索引，最终达到字符串结束处并失败。
当然人们有时会想要找到在输入中某点之后的一个字符串，例如，扫描包含多列数据的一个文本文件。目的导向执行也能起效：
write(5
只返回"the"出现在位置5之后的那些位置；否则比较会失败。成功的比较返回右手侧的结果，所以把find放置到这个比较的右手侧是重要的。
==搜集==
Icon包括了一些搜集类型，包括列表，它还可以用作堆栈和队列，表格（在其他语言中也叫做映射或字典），和集合等。Icon称它们为“结构”。搜集是固有的生成器，并可以使用“叹号语法”来轻易调用。例如：
lines :=                     # 建立一个空列表
while line := read() do {      # 循环从标准输入读取行
push(lines, line)            # 使用类堆栈语法来将行压入列表
}
while line := pop(lines) do {  # 循环于行可以从列表弹出之时
write(line)                  # 将行写出
}
使用如前面例子中见到的失败传播，可以组合测试和循环：
lines :=                     # 建立空列表
while push(lines, read())      # 压入直到为空
while write(pop(lines))        # 写直到为空
由于列表搜集是个生成器，可以使使用叹号语法进一步简化：
lines :=
every push(lines, !&input)
every write(!lines)
在这种情况下，在write内的叹号，导致Icon从数组一个接一个的返回一行文本，并且在结束处失败。&input是基于生成器的read的类似者，它从标准输入读取一行，所以!&input继续读取行直到文件结束。
因为Icon是无类型的，列表可以包含任何不同类型的值：
aCat := "muffins", "tabby", 2002, 8
在列表内的项目可以包括其他结构。为了建造更大的列表，Icon包括了list生成器；i := list(10, "word")生成包含"wold"的10个复本的一个列表。
就像其他语言中的数组，Icon允许项目按位置来查找，比如weight := aCat4。就像Array slicing那样，索引是在元素之间的，可以通过指定范围来获得列表的分片，比如aCat2:4产生列表"tabby",2002。
表格本质上是具有任意索引键而非仅为整数的列表：
symbols := table(0)
symbols"there" := 1
symbols"here" := 2
这个代码建立使用的0作为任何未知键的缺省值的一个table。接着向它增加了两个项目，具有键"there"和"here"，和分别的值1和2。
集合也类似于列表，但是只包含任何给定值的一个单一成员。Icon包括了++来产生两个集合的并集，**用于交集，和--用于差集。Icon包括一些预定义的Cset，即包含各种字符的集合。在Icon中有四个标准Cset：&ucase、&lcase、&letters和&digits。可以通过用单引号包围字符串来建造Cset，例如vowel : 'aeiou'.
==字符串==
在Icon中，字符串是字符的列表。作为一个列表，它们是生成器，并可以使用“叹号语法”来迭代：
write(!"Hello, world!")
这将在独立行上打印出字符串的每个字符。
子字符串可以使用在方括号内的一个范围规定从字符串中提取出来。范围规定可以返回到一个单一字符的一个点，或字符串的一个分片（slice）。字符串可以从左或从右索引。在一个字符串内的位置被定义为在字符之间：1A2B3C4，也可以从右规定：−3A−2B−1C0。例如：
"Wikipedia"1     == "W"
"Wikipedia"3     == "k"
"Wikipedia"0     == "a"
"Wikipedia"1:3   == "Wi"
"Wikipedia"-2:0  == "ia"
"Wikipedia"2+:3  == "iki"
这里最后例子采用了code|x1i1+:i2 : x2|"icon"表达式，产生x1在i1和i1 + i2之间的子字符串。
子字符串规定可以用作字符串内的左值。这可以被用来把字符串插入到另一个字符串，或删除字符串的某部份。例如：
s := "abc"
s2 := "123"
# s现在的值是"a123c"
s := "abcdefg"
s3:5 := "ABCD"
# s现在的值是"abABCDefg"
s := "abcdefg"
s3:5 := ""
# s现在的值是"abefg"
Icon的下标索引是在元素之间的。给定字符串s := "ABCDEFG"，索引是1A2B3C4D5E6F7G8。分片s3:5是在索引3和5之间的字符串，它是字符串"CD"。
==字符串扫描==
对处理字符串的进一步简化是“扫描”系统，通过?来发起，它在一个字符串上调用函数：
s ? write(find("the"))
Icon称呼?的左手端为“主语”，并将它传递到字符串函数中。所调用的find接受两个参数，查找的文本作为参数一，而要在其中查找的字符串是参数二。使用?，第二个参数是隐含的，而不由编程者来指定。在多个函数被依次调用在一个单一字符串上的常见情况下，这种风格可以显著的所见结果代码的长度并增加清晰性。
?不是简单的一种语法糖，它还为任何随后的字符串操作，建立一个“字符串扫描环境”。这基于了两个内部变量，&subject和&pos，这里的&subject是要扫描的字符串，而&pos是在这个主语字符串内的“游标”或当前位置。例如：
s := "this is a string"
s ? write("subject=",&subject," pos=",&pos,"")
将产生：
subject=this is a string pos=1
内建和用户定义的函数，可以被用于在要扫描的字符串上移动。所有内建函数缺省采用&subject和&pos，来允许用上扫描语法。比如函数"icon"，它设置扫描位置：产生code|&subject&pos:i|"icon"，并将i赋值到&pos。下列例子代码，写出在一个字符串内，所有空白界定出的word：
s := "this is a string"
s ? {                           # 建立字符串扫描环境
while not pos(0) do {         # 测试字串结束
tab(many(' '))              # 跃过任何空白
word := tab(upto(' ') | 0)  # 下一个word是直到下一个空白或行结束
write(word)                 # 写这个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。
一个更复杂的例子演示了在这个语言内生成器和字符串扫描的集成：
procedure main()
s := "Mon Dec 8"
s ? write(Mdate() | "not a valid date")
end
# 定义一个匹配函数
# 它返回匹配day month dayofmonth的一个字符串
procedure Mdate()
# 定义一些初始值
static dates
static days
initial {
days := "Mon","Tue","Wed","Thr","Fri","Sat","Sun"
dates := "Jan","Feb","Mar","Apr","May","Jun",
"Jul","Aug","Sep","Oct","Nov","Dec"
}
every suspend (retval
表达式"icon"计算x的大小。这里介入了内建函数"icon"，它匹配初始字符串：如果"icon"，产生"icon"，否则失败；它设定有缺省值：s2为&subject，如果s2缺省了，i1为&pos，否则为1，i2为0。