Icon (编程语言)
Icon是一门领域特定的高级编程语言，有着“目的（goal）导向执行”特征，和操纵字符串和文本模式的很多设施。它衍生自SNOBOL和SL5字符串处理语言。Icon不是面向对象的，但在1996年开发了叫做Idol的面向对象扩展，它最终变成了Unicon。
在1971年8月，SNOBOL的设计者之一离开了贝尔实验室，成为了亚利桑那大学的教授。他那时将SNOBOL4介入为研究工具。
作为最初在1960年代早期开发的语言，SNOBOL的语法带有其他早期编程语言的印记，比如FORTRAN和COBOL。特别是，语言是依赖列的，像很多要录入到打孔卡的语言一样，有着列布局是很自然的。此外，控制结构几乎完全基于了分支，而非使用块，而块在ALGOL 60中介入之后，已经成为了必备的特征。在他迁移到亚利桑那的时候，SNOBOL4的语法已然过时了。
Griswold开始致力于用传统的流程控制结构如codice_1，来实现SNOBOL底层的成功和失败概念。这成为了SL5，即“SNOBOL Language 5”的简写，但是结果不令人满意。在1977年，他考虑设计语言的新版本。他放弃了在SL5中介入的非常强力的函数系统，介入更简单的暂停和恢复概念，并为SNOBOL4自然后继者开发了新概念，具有如下的原则：
新语言最初叫做SNOBOL5，但因为除了底层概念外，全都与SNOBOL有着显著的差异，最终想要一个新名字。在这个时候Xerox PARC出版了他们关于图形用户界面的工作，术语“icon”从而进入了计算机词汇中。起初确定为“icon”而最终选择了“Icon”。
Icon语言衍生自ALGOL类的结构化编程语言，因而有着类似C或Pascal的语法。Icon最类似于Pascal的，是使用了codice_2语法的赋值，codice_3关键字和类似的语法。在另一方面，Icon使用C风格的花括号来结构化执行分组，并且程序开始于运行叫做codice_4的过程。
Icon还在很多方面分享了多数脚本语言（还有SNOBOL及SL5）的特征：变量不需要声明，类型是自动转换的，就说数字和字符串可以自动来回转换。另一个常见于很多而非全部的脚本语言的特征是，缺少行终止字符；在Icon中，不结束于分号的行，若其确有意义则由暗含的分号来终结。
过程是Icon程序的基本建造块。尽管它们使用Pascal名称，但工效上更像C函数并可以返回值；在Icon中没有codice_5关键字。 procedure doSomething(aString) end Icon的关键概念之一就是其控制结构基于表达式的“成功”或“失败”，而非大多数其他编程语言中的布尔逻辑。这个特征直接派生自SNOBOL，在其中表达式求值、模式匹配和模式匹配连带替换，都可以跟随着成功或失败子句，用来指定在这个条件下要分支到一个语句标签。例如，下列代码打印“Hello, World!”五次：
I = 1 LOOP OUTPUT = "Hello, World!" END
要进行循环，在索引变量codice_6之上调用内建的函数codice_7（小于等于），并且如果codice_8（成功），意味着codice_6小于等于codice_10，它分支到命名标签codice_11并继续下去。
Icon保留了基于成功或失败的控制流程的基本概念，但进一步发展了语言。一个变更是将加标签的codice_12式的分支，替代为面向块的结构，符合在1960年代后期席卷计算机工业的结构化编程风格。另一个变更是允许失败沿着调用链向上传递，使得整个块作为一个整体的成功或失败。这是Icon语言的关键概念。而在传统语言中，必须包括基于布尔逻辑的测试成功或失败的代码，并接着基于产出结果进行分支，这种测试和分支是固有于Icon代码的，而不需要明确的写出。考虑如下复制标准输入到标准输出的简单代码：
while a := read() then write(a)
它的含义是：“只要读取不返回失败，调用写出，否则停止”。在Icon中，codice_13函数返回一行文本或codice_14。codice_14不是简单的Java中的特殊返回值codice_16（文件结束）的类似者，因为它被语言依据上下文明确理解为意味着“停止处理”或“按失败状况处理”。这里即使codice_13导致一个错误它都会工作，比如说如果文件不存在。在这种情况下，语句codice_18会失败，而写操作简单的不调用。
成功和失败将沿着调用链向上传递，意味着可以将函数嵌入其他函数内，在失败时，它们整体停止。例如，上面的代码可以精简为： while write(read()) 在codice_13命令失败的时候，比如在文件结束之处，失败将沿着调用链上传，而codice_20也会失败。codice_21作为一个控制结构，在失败时停止。Icon称谓这个概念为“目的导向执行”，指称这种只要某个目的达到执行就继续的方式。在上面的例子中目的是读整个文件；读命令在有信息读到的时候成功，而在没有的时候失败。目的因此直接编码于语言中，不用再去检查返回码或类似的构造。
Icon使用目的导向机制用于进行传统的布尔测试，尽管有着微妙的差异。一个简单的比较如，这里的codice_22子句，不像在多数语言中那样意味着：“如果右侧运算求值为真”；转而它的意味更像是：“如果右侧运算成功”。在这种情况下，如果这个比较为真，codice_23算子成功。如果codice_22子句的这个表达式成功，则调用codice_25子句，如果它失败了，则调用codice_26子句或下一行。结果同于在其他语言中见到的传统codice_1，如果codice_28小于codice_29，codice_22进行codice_25子句。微妙之处是相同的比较表达式可以放置在任何地方，例如：
write(a < b) 另一个不同是codice_23算子如果成功，返回它的第二个实际参数，在这个例子中，如果codice_29大于codice_28，则导致它的值被写出，否则什么都不写。因为并非测试本身，而是一个算符返回一个值，它们可以串联在一起，允许像codice_35这样的事情 ，在多数语言中平常类型的比较下，必须写为两个不等式的结合，比如codice_36。
将成功和失败的概念与异常的概念相对比是很重要的；异常是不寻常的状况，不是预期的结果。在Icon中失败是预期的结果；到达文件的结束处是预期的状况而不是异常。Icon没有传统意义上的异常处理，尽管失败经常被用于类似异常的状况下。例如，如果要读取的文件的不存在，codice_37失败而不指示出特殊状况。在传统语言中，没有指示这些“其他状况”的自然方式，典型的异常处理是“抛出”一个值，下面是用Java处理缺失文件的例子： try { } catch (Exception e) {
这种情况需要两个比较：一个用于文件结束（EOF）而另一个用于所有其他错误。因为Java不允许异常作为逻辑元素来比较，就像Icon中那样，转而必须使用冗长的codice_38语法。codice_39块即使没有异常抛出，也强加了性能上的惩罚，Icon避免了这种。
目的导向执行的一个关键方面，是程序可能必须在一个过程失败时倒转到以前的状态，这个任务叫做回溯。例如，考虑设置一个变量为一个开始位置，并接着进行可以改变这个值的操作，这是在字符串扫描中常见情况，这里前进游标通过它所扫描的字符串。如果这个过程失败了，任何对这个变量的后续读取都返回最初的状态，而非被内部操纵后的状态是很重要的。对于这种任务，Icon有一个“可逆赋值”算子codice_40，和“可逆交换”算子codice_41。例如，考虑如下尝试在一个更大字符串内找到一个模式字符串的代码：
这个代码开始于移动codice_42到codice_43，这是查找的开始位置。但是，如果codice_44失败，这个块将作为整体失败，作为一个不想要的副作用，它导致codice_42的值留下为codice_43。故而应将codice_47替代为codice_48，指示codice_42在这个块失败时应当被重置为它以前的值。这提供了执行中的的类似者。
在Icon中表达式经常返回一个单一的值，例如codice_50，将求值并且如果codice_51的值小于codice_10则成功并返回codice_51，否则失败。但是，Icon还包括了过程不立即返回成功或失败，转而每次调用它们之时返回一个新值的概念。这些过程叫做生成器，并且是Icon语言的关键部份。在Icon的用语中，一个表达式或函数的求值产生一个“结果序列”。结果序列包含这个表达式或函数生成的所有可能的值。在结果序列被耗尽的时候，这个表达式或函数失败。
Icon允许任何过程返回一个单一值或多个值，使用codice_54、codice_55和codice_56关键字来控制。缺乏任何这种关键字的过程返回codice_14，它在执行进行到一个过程的codice_58处的时候发生。例如： procedure f(x) end 调用codice_59将返回codice_60，而调用codice_61将返回codice_14。这将导致不明显的行为，比如codice_63将什么都输出，因为codice_64失败而暂停了codice_65的操作。
将一个过程转换成一个生成器，要使用codice_56关键字，它意味着“返回这个值，并且在再次调用时，从这一点开始执行”。例如： procedure ItoJ(i, j) end 建立一个生成器，它返回一系列的数，开始于codice_42并结束于codice_68，接着在它们之后返回codice_14。codice_70停止执行，并返回codice_42的值，而不重置任何状态。当对相同函数做出另一次调用的时候，执行在这一点上拾起以前的值。在这种情况下，导致它进行codice_72，循环回到codice_21的开始处，并接着返回下一个值并再次暂停。这将持续直到codice_74失败，在这一点上它退出这个块并调用codice_54。这允许轻易的构造迭代器。
另一种类型的生成器建造器是“子”（alternator），它的感观和运算就像布尔算子codice_76，例如： if y < (x | 5) then write("y=", y)
这看起来是在说“如果codice_77小于codice_51或者codice_10那么...”，实际上它是生成器的一种简写形式，它返回值直到脱离于这个列表的结束处。这个列表的值被注入到运算之中，在这里是codice_23。所以这个例子，系统首先测试codice_81，如果codice_51实际上大于codice_77，它返回codice_51的值，这个测试通过，而codice_77的值在codice_25子句中写出。然而，如果codice_51不大于codice_77，它失败了，交替子继续，进行codice_89。如果这个测试通过，写出codice_77。如果codice_77不小于codice_51或者codice_10，交替子用完了，测试失败，codice_22子句失败，而不进行codice_65。因此，codice_77的值如果小于codice_51或codice_10，则它将出现在控制台上，从而履行了布尔codice_76的作用。函数不会被调用，除非求值它们的参数成功，所以这个例子可以简写为： write("y=", (x | 5) > y) 在内部，交替子不是简单的一个codice_76，它还可以用来构造值的任意列表。这可以用来在任意的一组值上迭代，比如： every i := (1|3|4|5|10|11|23) do write(i) codice_101类似于codice_21，循环经过一个生成器的返回的所有项目，在失败时退出。
因为整数列表在很多编程场景都是很常见的，Icon还包括了codice_103关键字来构造“事实上的”整数生成器： every k := i to j do write(k) 在这种情况下，从codice_42到codice_68的值，将注入到codice_65并写出多行输出。它可以简写为： every write(i to j) Icon不是强类型的，所以交替子列表可以包含不同类型的项目： every i := (1 | "hello" | x < 5) do write(i) 这将依赖于codice_51的值，写出codice_60、codice_109或可能的codice_10。
同样的“合取算符”codice_111，以类似于布尔算符codice_112的方式来使用： every x := ItoJ(0,10) & x % 2 == 0 do write(x) 这个代码调用codice_113并返回一个初始值codice_114，它被赋值给codice_51。接着进行合取的右手端，并且因为codice_116不等于codice_114，它写出这个值。接着再次调用codice_113生成器，它赋值codice_60到codice_51，这使得右手端失败而不打印任何东西。最终结果是从codice_114到codice_43的所有偶数的一个列表。
生成器的概念对于字符串操作是很强大的。在Icon中，codice_44函数是个生成器。下面的例子代码，在一个字符串中找出codice_124的所有出现位置： s := "All the world's a stage. And all the men and women merely players" every write(find("the", s)) codice_44在每次被codice_101恢复的时候，将返回codice_124的下一个实例的索引，最终达到字符串结束处并失败。
当然人们有时会想要找到在输入中某点之后的一个字符串，例如，扫描包含多列数据的一个文本文件。目的导向执行也能起效： write(5 < find("the", s)) 只返回codice_124出现在位置codice_10之后的那些位置；否则比较会失败。成功的比较返回右手侧的结果，所以把codice_44放置到这个比较的右手侧是重要的。
Icon包括了一些搜集类型，包括列表，它还可以用作堆栈和队列，表格（在其他语言中也叫做映射或字典），和等。Icon称它们为“结构”。搜集是固有的生成器，并可以使用“叹号语法”来轻易调用。例如： lines := [] # 建立一个空列表 while line := read() do { # 循环从标准输入读取行 while line := pop(lines) do { # 循环于行可以从列表弹出之时
使用如前面例子中见到的失败传播，可以组合测试和循环： lines := [] # 建立空列表 while push(lines, read()) # 压入直到为空 while write(pop(lines)) # 写直到为空 由于列表搜集是个生成器，可以使使用叹号语法进一步简化： lines := [] every push(lines, !&input) every write(!lines) 在这种情况下，在codice_65内的叹号，导致Icon从数组一个接一个的返回一行文本，并且在结束处失败。codice_132是基于生成器的codice_37的类似者，它从标准输入读取一行，所以codice_134继续读取行直到文件结束。
因为Icon是无类型的，列表可以包含任何不同类型的值： aCat := ["muffins", "tabby", 2002, 8] 在列表内的项目可以包括其他结构。为了建造更大的列表，Icon包括了codice_135生成器；codice_136生成包含codice_137的codice_43个复本的一个列表。
就像其他语言中的数组，Icon允许项目按位置来查找，比如codice_139。就像那样，索引是在元素之间的，可以通过指定范围来获得列表的分片，比如codice_140产生列表codice_141。
表格本质上是具有任意索引键而非仅为整数的列表：
symbols := table(0) symbols["there"] := 1 symbols["here"] := 2
这个代码建立使用的codice_114作为任何未知键的缺省值的一个codice_143。接着向它增加了两个项目，具有键codice_144和codice_145，和分别的值codice_60和codice_147。
集合也类似于列表，但是只包含任何给定值的一个单一成员。Icon包括了codice_148来产生两个集合的并集，codice_149用于交集，和codice_150用于差集。Icon包括一些预定义的codice_151，即包含各种字符的集合。在Icon中有四个标准codice_151：codice_153、codice_154、codice_155和codice_156。可以通过用单引号包围字符串来建造codice_151，例如codice_158.
在Icon中，字符串是字符的列表。作为一个列表，它们是生成器，并可以使用“叹号语法”来迭代：
write(!"Hello, world!")
这将在独立行上打印出字符串的每个字符。
子字符串可以使用在方括号内的一个范围规定从字符串中提取出来。范围规定可以返回到一个单一字符的一个点，或字符串的一个分片（slice）。字符串可以从左或从右索引。在一个字符串内的位置被定义为在字符之间：codice_159，也可以从右规定：codice_159。例如：
"Wikipedia"[1] == "W" "Wikipedia"[3] == "k" "Wikipedia"[0] == "a" "Wikipedia"[1:3] == "Wi" "Wikipedia"[-2:0] == "ia" "Wikipedia"[2+:3] == "iki"
这里最后例子采用了表达式，产生codice_161在codice_162和codice_163之间的子字符串。
子字符串规定可以用作字符串内的左值。这可以被用来把字符串插入到另一个字符串，或删除字符串的某部份。例如： s := "abc" s[2] := "123" s := "abcdefg" s[3:5] := "ABCD" s := "abcdefg" s[3:5] := ""
Icon的下标索引是在元素之间的。给定字符串codice_164，索引是codice_165。分片codice_166是在索引codice_167和codice_10之间的字符串，它是字符串codice_169。
对处理字符串的进一步简化是“扫描”系统，通过codice_170来发起，它在一个字符串上调用函数： s ? write(find("the")) Icon称呼codice_170的左手端为“主语”，并将它传递到字符串函数中。所调用的codice_44接受两个参数，查找的文本作为参数一，而要在其中查找的字符串是参数二。使用codice_170，第二个参数是隐含的，而不由编程者来指定。在多个函数被依次调用在一个单一字符串上的常见情况下，这种风格可以显著的所见结果代码的长度并增加清晰性。
codice_170不是简单的一种语法糖，它还为任何随后的字符串操作，建立一个“字符串扫描环境”。这基于了两个内部变量，codice_175和codice_176，这里的codice_175是要扫描的字符串，而codice_176是在这个主语字符串内的“游标”或当前位置。例如： s := "this is a string" s ? write("subject=[",&subject,"] pos=[",&pos,"]")
将产生：
subject=[this is a string] pos=[1] 内建和用户定义的函数，可以被用于在要扫描的字符串上移动。所有内建函数缺省采用codice_175和codice_176，来允许用上扫描语法。比如函数，它设置扫描位置：产生，并将codice_42赋值到codice_176。下列例子代码，写出在一个字符串内，所有空白界定出的codice_183：
s := "this is a string" s ? { # 建立字符串扫描环境 这个例子介入了一些新函数。codice_184返回codice_176的当前值。为何需要这个函数，而不简单的直接使用codice_176的值，不是显而易见的；原因是codice_176是一个变量，而不能呈现值codice_14，而过程codice_184能。因此codice_184提供对codice_176的轻量级包装，它允许轻易使用Icon的目的导向控制流，而不用针对codice_176提供手写的布尔测试。在这种情况下，测试是“codice_176是零”，在Icon的字符串位置的特异编码中，零是行结束。如果它不是零，codice_184返回codice_14，它通过codice_196反转而使得循环继续。
codice_197从当前codice_176开始，找到提供的codice_151参数的一个或多个例子。在这种情况下，它查找空格字符，所以这个函数的结果是在codice_176之后的第一个非空格字符的位置。codice_201移动codice_176到那个位置，这种情况下再次具有潜在的codice_14，例如codice_197在字符结束处脱离。codice_205本质上是codice_197的反函数；它返回紧前于提供的codice_151的例子的位置，接着由另一个codice_201来设置codice_176。这里的交替用来在行结束处也停止。
这个例子通过使用更合适的“字分隔”codice_151，可以包括句号、逗号和其他标点，还有其他如tab和不换行空格，能够变得更加健壮。这个codice_151可以接着用于codice_197和codice_205。
一个更复杂的例子演示了在这个语言内生成器和字符串扫描的集成：
procedure main() end procedure Mdate() static dates static days initial { every suspend (retval