logo
天地变化的道理
使用率很高网站
生活要常常分享
您身边百科全书
免费为您秀产品
Scheme
Scheme Scheme是一种函数式编程语言,是Lisp的两种主要方言之一,不同于与之并列的Common Lisp,Scheme遵循哲学,以一个小型语言核心作为标准,加上各种强力语言工具(语法糖)来扩展语言本身。Scheme是第一个使用静态作用域的Lisp方言,也是第一个引入头等续体和“干净宏”的编程语言。 简介. 在1975年,麻省理工学院的杰拉德·杰伊·萨斯曼与盖伊·史提尔二世,开发出了Scheme语言最初版本,随后两人通过发表「λ论文集」而不断对它进行完善和推广。Scheme与λ演算关系十分密切,故将小写字母「λ」用作标志。 麻省理工学院与其他一些院校,曾采用Scheme教授计算机科学入门课程。著名的入门教材《-{zh-hans:计算机程序的构造和解释;zh-hant:电脑程式的构造和解释}-》(SICP),利用Scheme来诠释程序设计。Scheme有众多实现可视为一个主要优势,然而不同实现之间的差异成为了它的一个劣势,Scheme掌控委员会声称,它是“世上最不可移植的编程语言”,并且是一个“编程语言家族”而非一个单一的语言。 历史. 起源. Guy Lewis Steele Jr. Scheme起源于1958年由约翰·麦卡锡提出的Lisp语言。麦卡锡通过Lisp证明了,经由几个简单的算子,与用作匿名函数的借鉴自阿隆佐·邱奇的λ表示法,就可以构建出图灵完备的系统。麦卡锡提出的S-表达式,可以将程序与数据用相同的结构存储,这被称为同像性。Scheme的语法即来自S-表达式,这一特性使得在Scheme中实现自循环直译器变得非常简单。 在1973年,麻省理工学院的提出的一种叫做演员模型的计算模型,并用Lisp开发当时叫做-73的新语言来实现它。杰拉德·萨斯曼与盖伊·史提尔为了理解演员模型,决定在Maclisp工作环境中实现一个微型Lisp解释器,并接着增加创建演员和发送消息的机制。两人很快发现了演员模型与λ演算之间的相似性,所谓「演员」就是彼得·兰丁提出的闭包,而在1970年已将它介入Lisp用来解决;故而实现演员的关键,是将词法作用域介入到Lisp中。 在1975年,基于对λ演算表达能力的理解,杰拉德·萨斯曼与盖伊·史提尔开发出了新型Lisp解释器,并将在其上完成的微缩版的演员实现命名为「Schemer」,后因作业系统文件名的字符数限制而改为Scheme。尽管Hewitt指出了Scheme不能表达特定类型的原语演员,Scheme解释器本身采用的简约的语法和语义,很快赢得广泛接受。 在1978年,盖伊·史提尔二世和杰拉德·杰伊·萨斯曼发表了《修订的Scheme报告:一种LISP方言》,从而完善了Scheme的功能,并正式将其确立为Lisp的一种主要方言。在1998年,二人在总结Scheme历史时指出,简单而强大的λ演算,使得Scheme最终得以实现极度的精简化。 λ论文集. 「λ论文集」是杰拉德·萨斯曼与盖伊·史提尔撰写的关于Scheme的一系列论文,最早作为麻省理工学院的内部备忘录发表。通常认定λ论文集包括: 标准化. Scheme的业界标准,是由专门的掌控委员会发表的《第n次修订的演算法语言Scheme报告》(Revisedn Report on the Algorithmic Language Scheme),这种标题形式参照了ALGOL 60标准文档的标题。Scheme曾经由IEEE标准化为IEEE 1178–1990,它于2019年11月07日停用。 1998年通过的R5RS是现在被普遍接受的标准。2007年通过的R6RS,做出了很大的变动,Scheme社区中有使用者指责它在堆积华而不实的功能。Scheme掌控委员会决定在R7RS中,将Scheme分化为两个独立而兼容的语言:一个是2013年通过的R7RS-small,它为教学场合提供对IEEE/R5RS标准的及时更新,R7RS-small目前已经有了一些实现支持;而另一个是作为标准库合集的R7RS-Large,它包含了各种提议被接纳并进入冻结状态的(SRFI),以此为实际编程场合提供对R6RS标准的继续完善,Larceny和Chibi提供了R7RS-Large过渡草案红色版(即数据结构部份)的全部SRFI的实现。 命名法. 在正式场合比如Scheme标准的中,提及一个λ表达式或原始过程时,偏好使用词语“过程”而非“函数”。在一般使用中,词语“过程”和“函数”是可互换使用的。过程应用有时被正式称呼为“组合”(combination)。 同其他Lisp一样,在Scheme中使用术语“”,来提及没有实际参数的过程。术语“适当”(proper)尾递归,称谓所有Scheme实现的一个性质,它们都进行尾递归优化,从而支持无限数目的活跃尾递归。 基础特征. Scheme大体上是一个函数式程式语言,并支援其他编程范型。它同其他Lisp编程语言家族语言共享了很多特征。它的非常简单的语法基于Lisp的S-表达式:即圆括号包围的列表,其中的前缀是算符,而随后那些的是实际参数。故而Scheme程序由嵌套的列表的序列构成。列表也是Scheme中的主要数据结构,这导致了在源代码和数据格式之间的紧密等价性,即同像性。Scheme程序可以轻易的动态建立和求值Scheme代码片段。 Scheme与其他Lisp方言的列表,都是基于最基础的数据结构有序对(pair)。Scheme从它的Lisp先辈继承了一组丰富的列表处理原始运算,比如codice_1、codice_2和codice_2。Scheme的变数都使用动态强型别系统,Scheme中的过程都是头等物件,即头等函数,因此过程可以作为值赋值给变量,或作为实际参数传递给过程。 本章主要集中于语言的创新特征,它们使得Scheme区别于其他Lisp方言。除非专门指出,这里描述的特征都有关于R5RS标准。在本章例子中使用“codice_4”表示法,来指示求值在紧前行上的表达式的结果。这同于R5RS中使用的约定。本章节描述的特征使得Scheme不同于在它之前的其他编程语言。Scheme的这些方面强烈的影响了Scheme语言的所有产品,并共通于始自1975年最初的λ论文,特别是自从R2RS以来,所有版本的Scheme编程语言。 极简主义. Scheme的简约性,使它成为具备同级别功能的程式语言中最易于实现的语言,这得益于使用λ演算来从更原始的形式导出多数的语言语法。例如在R5RS标准中,定义了23个基于S-表达式的语法构造,其中14个被归类为派生形式或库形式,它们可以被写为涉及原则上为lambdad的更基础形式的宏。正如R5RS(§3.1)所说:“最基础的变量绑定构造是lambda表达式,因为所有其他的变量绑定构造,都可以依据lambda表达式来做出解释”。 基础形式:codice_5、codice_6、codice_7、codice_8、codice_9、codice_10、codice_11、codice_12、codice_13 派生形式:codice_14、codice_15、codice_16、codice_17、codice_18、codice_19、codice_20、codice_21、codice_22、命名codice_15、codice_24、codice_25、codice_26、codice_27 下面是一个宏的例子,它将codice_15实现为使用codice_6来进行变量绑定的一个表达式: (define-syntax let (syntax-rules () ((let ((var expr) ...) body ...) ((lambda (var ...) body ...) expr ...)))) 这里的codice_30称为“模式”,模式中起始的codice_15被称为“关键字”,codice_32的空括号中可添入作为辅助关键字的叫做“文字”的标识符,codice_33、codice_34和codice_35称为“模式变量”,codice_36是称为“省略号”的标识符,它表示所跟随的子模式可以匹配零或多个输入中的元素;而codice_37称为“模板”。使用上述定义的codice_15,一个Scheme实现可以将codice_39,重写为codice_40,这将实现任务缩减为编码过程实例化的工作。 词法作用域. 不像更早期的Lisp比如Maclisp,Scheme像多数现代语言一样,采用了词法作用域:在一个程序单元中所有可能的变量绑定,都可以通过阅读这个程序单元来分析出来,而不需要考虑到它可能在其中被调用的那些上下文。这对比于动态作用域,它是早期Lisp方言的特征,因为在当时的编译器和解释器中,用来实现词法作用域算法的原始的文字替换方法,关联着处理代价。在动态作用域的Lisp中,对一个过程内的自由变量的引用,依赖于这个调用的上下文,完全有可能引用到这个过程外部的相当不同的绑定。 λ演算. 邱奇的λ演算的数学表示法,启发了Lisp使用codice_6作为介入一个过程的关键字,并影响了Lisp中涉及到使用高阶函数的函数式编程技术的发展。但是早期的Lisp由于对自由变量的处理方式,而不适合表达λ演算。 一个正式的λ系统,拥有一些公理和一个完备的推理规则。这有助于使用数理逻辑和工具来进行分析。在这个系统中,演算可以被看作直接的演绎。λ演算的语法,来自用codice_42、圆括号、空格、点号和符号λ形成的递归表达式。λ演算的功能包括:首先,充当强力的数理逻辑的起点。其次,它可以缩减编程者在考虑实现细节上的要求,因为它可以用于模拟机器求值。最后,λ演算建立了一个坚实的元理论。 在Scheme中,codice_6关键字被用于定义匿名过程,并且使用codice_5基础形式来定义命名过程,即将一个codice_6过程绑定到指名的全局变量。在Scheme中,codice_46与codice_47,在语法上是等同的。例如有序对可以表示为: (lambda (m) (m x y))) (z (lambda (p q) p))) (z (lambda (p q) q))) 这样定义出来的codice_1、codice_49和codice_50,满足恒等式codice_51等于codice_52,和codice_53等于codice_54。甚至递归也可以表示为利用λ演算的Y组合子。 词法作用域的介入,通过在某些形式的λ表示法,和在工作编程语言中它们的实际表达之间做出等价,解决了早期Lisp的有关问题。Sussman和Steele展示了,通过将λ表达式用作“控制结构和环境修改器”,而非简单的过程实例化,可以用这个新语言优雅的导出其他编程语言包括ALGOL和Fortran的,所有指令式和声明式语义,和其他Lisp的动态作用域。他们在第一篇λ论文中,与对Scheme的首次描述一起,介入了,并在后续的论文中,他们推进演示了在这种实际使用中体现出的λ演算的原生能力。 过程应用中的求值次序. Scheme采用了传值调用,但不同于多数Lisp规定了过程实际参数的求值次序,Scheme不规定求值次序。对比于其他Lisp,Scheme表达式在算符位置(第一个项目)上可以是一个表达式,只要在算符位置上的表达式的结果是一个过程,这种表示就是完全合法的。 在Scheme中,在算符和实际参数位置上的这些表达式的求值次序,可以在逐个调用的基础上由实现来选择,而唯一的约束是:“运算符和运算数表达式的任何并发求值的效果,被约束为一致于某种顺序次序的求值。”(R5RS sec. 4.1.3) (let ((ev (lambda (n) (display "Evaluating ") (display (if (procedure? n) "procedure" n)) (newline) n))) ((ev +) (ev 1) (ev 2))) codice_55是一个过程,它描述传递给它的实际参数,接着返回这个实际参数的值。在调用过程codice_56来加codice_57和codice_58之中,表达式codice_59、codice_60和codice_61可以按任何次序求值,只要效果上它们不像是并行求值的。因此在上述例子被执行的时候,标准Scheme可以按任何次序来显示前三行: Evaluating procedure Evaluating 1 Evaluating 2 3 然而一行的文本不可以同另一行的文本产生交错,因为这会违反顺序求值约束。 块结构. Scheme的代码块结构,不同于早先Lisp的语言构造。自从R2RS开始,Scheme中的块,是通过三个“绑定构造”来实现的:、codice_16和codice_17。例如,下列构造建立一个块,其中叫做codice_33的符号被绑定到数codice_65: ;; 语句走到这里时,任何到var的引用都会绑定到10 依据编程者的需要,块可以来建立任意复杂的块结构。使用块结构来建立局部绑定,减轻了不然可能发生的。 codice_15的变体之一codice_16,允许绑定引用在前面相同构造中定义的变量,例如: (let* ((var1 10) (var2 (+ var1 12))) ;; 但是var1的定义不可以引用var2 codice_15的另一个变体codice_17,被设计用来确使互递归过程可绑定彼此。 下面的例子是侯世达: (letrec ((female (lambda (n) (if (= n 0) 1 (- n (male (female (- n 1))))))) (male (lambda (n) (if (= n 0) 0 (- n (female (male (- n 1)))))))) (let loop ((i 0)) (if (> i n) (cons (cons (female i) (male i)) (loop (+ i 1))))))) ===> ((1 . 0) (1 . 0) (2 . 1) (2 . 2) (3 . 2) (3 . 3) (4 . 4) (5 . 4) (5 . 5)) 在一个单一的codice_17中的所有过程,可以通过名字引用其他的过程,还有在相同的codice_17中此前定义的变量的值,但是它们不可以引用在相同的codice_17中此后定义的变量的值。 codice_15的变体“命名codice_15”形式,在codice_15关键字之后有一个标识符。它将这些codice_15变量绑定到一个过程的实际参数,这个过程的名字由这个标识符给出,它的过程体是codice_15形式的主体。这个过程体可以通过调用这个过程达成想要的重复。命名codice_15被广泛用于实现迭代。 一个简单的计数器例子: (if (> n 10) (cons n (loop (+ n 1))))) ===> (1 2 3 4 5 6 7 8 9 10) 就像Scheme中的任何过程一样,在命名codice_15中建立的这个过程是头等对象。 适当尾递归. Scheme拥有一个迭代构造codice_14,但是在Scheme中更推崇的,是使用尾递归来表达迭代。遵循标准的Scheme实现被要求优化尾递归,从而支持无限数量的活跃尾递归(R5RS sec. 3.5),这个性质被Scheme报告描述为“适当”(proper)尾递归,它使得Scheme编程者可以安全的使用递归结构来书写迭代算法,这在很多时候是更符合直觉的。尾递归过程和命名codice_15形式,提供对使用尾递归的迭代的支持。 (let loop ((i n) (res '())) (if ( (0 1 4 9 16 25 36 49 64 81) 头等续体. 在Scheme中续体是头等对象。自从R2RS开始,Scheme提供了过程codice_82,简写为codice_83,它捕获当前续体,将其包装成逃脱(escape)过程,再绑定到编程者提供的一个过程的唯一形式参数上。如果逃脱过程在后来某个时候被调用,它会放弃在后来时候正生效的任何续体,转而使用在创建这个逃脱过程之时生效的那个续体。逃脱过程与原本调用codice_83的续体,接受相同数目的实际参数;除了用codice_85创建的续体,所有续体恰好接受一个值(R5RS sec. 6.4)。 头等续体使得编程者能够建立非局部控制结构比如迭代器、协程和回溯。续体可以被用来模拟指令式编程语言中return语句的行为。下列函数codice_86接受函数codice_87和列表codice_88,返回codice_88中的第一个元素codice_52,它使得codice_91返回真。 (call/cc (lambda (return) (for-each (lambda (x) (if (func x) (return x))) lst) #f))) ===> 7 ===> #f David Madore在1999年提出的阴阳谜题,展示了Scheme可以将续体当作头等对象处理,绑定它们到变量,和把它们作为给过程的实际参数来传递。 统一的命名空间. 对比于Common Lisp,在Scheme中所有的数据和过程共享一个共同的命名空间,而Common Lisp中有函数和数据分离的命名空间,使得一个函数和一个变量可以有相同的名字,并且将一个函数作为值引用时要求特殊的表示法。这有时叫做“Lisp1与Lisp2”差异,二者分别称谓Scheme的统一的命名空间,和Common Lisp的分立的命名空间。 在Scheme中, 可以使用操纵和绑定数据的原始运算来绑定过程。没有等价于Common Lisp的codice_92和codice_93的原始运算。 f ===> 10 f ===> 26 ===> 18 f ===> 13 ===> 21 ===> (101 102 103) 实现标准. 本章归档多年来做出的给与Scheme特定特征的设计决定,它们不是最初设计的直接产物。 注释. 直到R5RS标准,在Scheme中的标准注释是分号,它使得这行余下部份对Scheme不可见。许多实现支持可替代的约定,允许注释扩展为多于一行,而R6RS标准允许其中的两种:一个完整的S-表达式,可以通过前导codice_94 而变成一个注释(介入于SRFI 62),和通过用codice_95和codice_96围绕文本,产生的“多行注释”或“块注释”。 在布尔表达式中非布尔值的处理. 在多数Lisp方言包括Common Lisp中,布尔表达式中的值codice_97按照惯例被求值为值假。在Scheme中,自从1991年的IEEE标准,除了codice_98的所有的值,包括在Scheme中写为codice_99的codice_97的等价者,在布尔表达式中求值为值真(R5RS sec. 6.3.1)。 在多数Lisp中表示布尔值真的常量是codice_101,而在Scheme中它是codice_102。 干净宏. Scheme的语法可以轻易的通过宏系统来扩展。R5RS标准介入了强力的干净宏系统,它允许编程者使用一种简单的模式匹配子语言,向语言增加的新的语法构造(R5RS sec 4.3)。在此前的R4RS标准中,干净宏系统已经作为“高级”系统,和“低级”宏系统一起被编排入标准的附录中,二者都被当作对Scheme的扩展而非语言的本质部份。 干净宏的实现,也叫做codice_12, 被要求遵守语言的其余部份的词法作用域。这是通过针对宏展开的特殊命名和作用域规则来确保的,从而避免在其他编程语言的宏系统中可能出现的常见编程错误。R6RS规定了更加复杂的变换系统codice_104,它已经作为对R5RS Scheme的一个语言扩展而能够获得到有一段时间了。例如: (define-syntax when (syntax-rules () ((when pred exp exps ...) (if pred (begin exp exps ...))))) 宏和过程的调用看起来非常相似,二者都是S-表达式,但是它们被不同的对待。在编译器遇到程序中的一个S-表达式的时候,它首先查看这个符号是否被定义为在当前词法范围内的语法关键字。如果是这样,它接着尝试展开这个宏,将在这个S-表达式尾部的项目当作实际参数,而不用编译代码来求值它们,递归的重复这个处理过程直到没有余留的宏调用。如果它不是一个语法关键字,编译器编译代码来求值在这个S-表达式尾部的实际参数,并接着求值在这个S-表达式头部的符号所表示的变量,把它作为过程来调用,并把最终的尾部表达式作为实际参数传递给它。 多数Scheme实现还提供额外的宏系统。其中最流行是语法闭包、显式重命名宏和codice_105,它是类似于Common Lisp中提供的codice_106系统的非干净宏。 不能指定一个宏是否为干净的,是宏系统的一个缺点。可作为替代的展开模型比如作用域集合,提供一种潜在的解决方案。 延迟求值. 自从R2RS开始,Scheme通过codice_24形式和过程codice_108支持延迟求值。 ===> 22 (force eval-aplus50)) ===> 70 ===> 22 codice_24原始运算,产生叫做promise的一个对象,它在将来的某个时间点上被询问从而递交结果值,promise的原本定义的文字内容会被留存,并且在第一次使用codice_108之后它的值也会被留存。promise永远只求值一次。它们可以被用来实现高级的惰性求值构造比如串流。 在R5RS中,给出了codice_24和codice_108的推荐实现,将promise实现为没有实际参数的一个过程(),并使用记忆化来确保它永远只求值一次,与调用codice_108的次数无关(R5RS sec. 6.4)。在R6RS标准中,它们不再是原始运算,转而作为R5RS兼容库的一部份提供(rnrs r5rs (6))。 SRFI 41确使表达有限和无限序列能够具有非凡的经济性。例如,这是使用在SRFI 41中的函数定义的一个斐波那契数列: (define fibs (stream-cons 0 (stream-cons 1 (stream-map + fibs (stream-cdr fibs))))) ===> 218922995834555169026 eval及其运行环境. R5RS标准之前,在其他Lisp中无处不在的codice_114过程,在Scheme中没有等价者。在第一篇λ论文中,曾经将codice_115描述为“类似于LISP函数EVAL”,但在具有词法作用域的Scheme中,对这个表达式在哪个环境中求值存在困惑。例如,不明确求值下列表达式的结果应当是codice_116还是codice_117: (let ((+ *)) (evaluate (list name 2 3)))) 在求值实际参数codice_118的时候,在外层环境中找到了它的定义;在求值结果表达式codice_119的时候,如果在外层环境中求值,结果是两个运算数的总和;如果在内层环境中求值,这里符号codice_56已经被绑定到过程codice_121的值,结果是两个运算数的乘积。为此在1978年的最初修订报告中,将codice_115替代为codice_123,它接受分别为代码和运行环境的两个实际参数。由于各种技术和实际原因,第二次、第三次和第四次修订报告省略了任何codice_114的等价者。 R5RS通过规定三个返回环境的过程,并提供接受一个S-表达式和一个环境的过程codice_114,它在提供的这个环境内求值这个表达式(R5RS sec. 6.5),解决了这个困惑。R6RS通过提供叫做codice_126的一个过程进行了扩展,编程者通过它可以精确的指定将哪个对象导入到求值环境之内。 通过现代Scheme(通常兼容于R5RS)来求值表达式codice_34,你需要定义如下这样的一个函数codice_115: (eval expr (interaction-environment))) codice_129是解释器的全局环境。 数值塔. Scheme规定了数值数据类型的相较完全的集合,包括复数和有理数类型,这在Scheme中叫做“数值塔”(R5RS sec. 6.2)。标准对它们作抽象处理,而不委以任何特定的内部表示。 数可以有精确性品质。一个精确数只能从涉及其他精确数的精确运算产生,不精确是传染的。标准规定任何两个实现对产生精确数的所有运算都必须产生等价的结果。 R5RS标准规定了过程codice_130和codice_131,它们可以用于改变一个数的精确性。codice_131产生“在数值上最接近实际参数的精确数”。codice_130产生“在数值上最接近实际参数的不精确数”。R6RS标准在主报告中省略了这些过程,而是在标准库中将它们规定为R5RS兼容过程(rnrs r5rs (6))。 在R5RS标准中,不要求Scheme实现去实现整个数值塔,而是它们必须实现“符合实现用途和Scheme语言精神二者的连贯子集”(R5RS sec. 6.2.3)。新的R6RS标准要求实现整个数值塔,并且“精确整数对象和精确有理数对象实际上有无限的大小和精度,并且对于实现特定过程...所以它们在得到精确实际参数时总是返回精确结果”(R6RS sec. 3.4, sec. 11.7.1)。 在支持精确有理复数的实现中的精确算术例子: x ===> 509/60+1/3i ===> #t 在既不支持精确有理数也不支持复数却接受有理数表示法的实数的实现中相同算术的例子: xr ===> 8.48333333333333 xi ===> 0.333333333333333 ===> #f ===> #f 二者实现都符合R5RS标准,但是第二个实现不符合R6RS,因为它没有实现完整的数值塔。 原始数据类型不相交. 在Scheme中原始数据类型是不相交的。对于任何Scheme对象,下列谓词中只有一个可以为真:codice_134、codice_135、codice_136、codice_137、codice_138、>codice_139、codice_140、codice_141和codice_142(R5RS sec 3.2)。 作为对比,在数值数据类型之内,对数值值是可以有交叠的。例如,一个整数值可以同时满足如下谓词:codice_143、codice_144、codice_145、codice_146和codice_137(R5RS sec 6.2)。 等价谓词. Scheme在任意对象之间有三种不同类型的等价,它们通过三种不同的“等价谓词”来指示,即测试等式的关系算符codice_148、codice_149和codice_150: 在Scheme中还存在依赖于类型的等价运算:codice_158和codice_159比较两个字符串(后者进行不依赖大小写比较);codice_160和codice_161比较字符;codice_162比较数。 输入/输出. Scheme的输入和输出基于了“端口”数据类型(R5RS sec 6.6)。R5RS定义了两个缺省端口,可通过过程codice_163和codice_164来访问,它们对应于Unix概念的标准输入和标准输出。多数实现还提供codice_165。标准通过标准过程比如codice_166和codice_167,支持输入和标准输出的重定向。多数实现提供有重定向能力的字符串端口,使用在SRFI 6中描述的过程,确使很多常规的输入-输出运算能在字符串缓冲区上进行而非在文件上。R6RS标准规定了更多复杂和有能力的端口过程和很多新的端口类型。 下面的例子是使用严格的R5RS Scheme书写的。 例子1,缺省输出到codice_164: (hello0)) 例子2,类似例子1但对输出过程使用可选的端口参数的例子: (hello1 (current-output-port))) 类似例子1,但是输出被重定向到一个新建文件: (with-output-to-file "helloworldoutputfile" hello0)) 类似例子2,但是具有显式的文件打开和端口关闭来发送输出到文件: (output-port (open-output-file "helloworldoutputfile"))) (hello1 output-port) (close-output-port output-port)) 类似例子2,但是使用codice_169来发送输出到一个文件: (call-with-output-file "helloworldoutputfile" hello1)) 对输入提供了类似的过程。R5RS Scheme提供了谓词codice_170和codice_171。对于字符输入和输出提供了codice_172、codice_173、codice_174和codice_175。为了书写和阅读Scheme表达式,Scheme提供了codice_176和codice_177。在读运算时,如果输入端口到达了文件的结束处,则返回的结果是codice_178对象,并且这可以使用谓词codice_179来测试。 除了标准之外,SRFI 28定义了一个基本的格式化过程,类似Common Lisp的codice_180并以此命名。 标准过程的重定义. 在Scheme中,过程被绑定到变量。在R5RS中语言标准正式的授权编程者可以变更内建过程的变量绑定,在效果上重定义它们(R5RS "Language changes")。例如,通过重定义codice_56可以将它扩展为同接受数一样接受字符串: (set! + (let ((original+ +)) (lambda args (apply (if (or (null? args) (string? (car args))) string-append original+) args)))) ===> 6 ===> "123" 在R6RS中所有绑定,包括标准过程,都属于某个库,并且所有导出的绑定都是不可变的(R6RS sec 7.1)。因此,禁止通过变更进行标准过程的重定义。转而,可以在标准过程的名字下导入不同的过程,这在效果上类似于重定义。 标准形式和过程概述. Scheme语言正式定义于1998年的R5RS和2007年R6RS标准之中。它们描述了标准“形式”,即关键字和随同的语法,它们提供语言的控制结构,和执行通常任务的标准过程。 在标准Scheme中,从一个数据类型转换到另一个数据类型的过程,在它们的名字中包含字符串codice_182,谓词结束于一个codice_183,而改变已经分配了数据的值的过程结束于一个codice_184。Scheme编程者通常跟从这些命名约定。 标准形式. 本表格描述Scheme中的标准形式。某些形式出现在不止一行之中,因为它们不能被轻易的归类入语言中的一个单一功能。 在表格中标记了“L”的形式被归类为标准中的派生的“库”形式,并且在实践中通常被实现为使用了更基础形式的宏,这使得实现任务比在其他语言中要容易许多。 注意codice_22在R5RS中被定义为库语法,但是展开者需要知道它来完成分片功能。在R6RS中它不再是库语法。 标准过程. 下面两个表格描述在R5RS Scheme中的标准过程。R6RS更加具有可扩展性而如此总结是不实际的。 某些过程出现在不止一行之中,因为它们不能轻易的分类入语言的一个单一功能。 在其名字中包含codice_186的字符串和字符过程,在它们的实际参数之间进行不依赖大小写的比较:相同字符的大写和小写版本被认为是相等的。 接受多于二个实际参数的codice_187和codice_188在R5RS中被定义了但留作为可选的。 Scheme实现要求. 由于Scheme的极简主义,很多常见过程和语法形式不是由标准定义的。 为了保持核心语言很小并促进扩展的标准化,Scheme社群拥有“Scheme实现要求”(SRFI)流程,通过对扩展提案的仔细研讨来定义扩展库。这提升了代码可移植性。很多SRFI被几乎所有Scheme实现支持。 在不同的实现中具有相当广泛支持的SRFI包括: 实作. Scheme的精简设计,使得程式语言设计人士与爱好者,特别钟爱研究它,故而它有不断涌现的新实作,而活跃开发的实作也在持续跟进语言标准更新。尽管Scheme有众多实现是它的一个主要长处,但是由于Scheme没有库函数标准,造成了很多不可互通的实作互相竞争,试图使用Scheme编写既复杂又便于移植的程序,往往比较困难。R6RS和R7RS-Large,在核心语言之外制定了库函数标准,使得编译器开发者和贡献者可以实现Scheme的可移植库。 几乎所有Scheme实作都有传承自Lisp的「读取﹣求值﹣输出循环」交互模式,一些Scheme实作亦可作为编译器。很多用C语言及衍生语言写成的软体,都利用Scheme作为脚本语言,很多嵌入式系统语言即是基于Scheme。Chez Scheme和Larceny等实现,可以将Scheme程式编译为本机二进制码。Gambit和Chicken等实现,可将Scheme程式编译为C代码,还能编译成JVM字节码或者.Net字节码。 一些实现支持额外的特征。比如提供与Java类的集成,而Scheme到C编译器通常易于使用C写成的外部库,甚至允许将实际C代码嵌入到Scheme源代码中。另一个例子是用java书写的,它提供了支持Scheme学习的一组可视工具。 教科书. 实际用处. 很多著名的电脑科学院校都利用Scheme来教授入门级课程。以下为一些最为著名的教授Scheme的学校: 自由软体影像处理程式GIMP利用Scheme为嵌入式脚本语言。GNOME中有到核心库的一个GNU的扩展语言Guile包装器。在2012年出现的Julia所采用的语法解析器,是用Scheme方言Femtolisp实现的。 外部链接. -{H|zh-hans:汇编语言;zh-hant:组合语言}- -{H|zh-hans:标记语言;zh-hant:置标语言}-
Scheme
本站由爱斯园团队开发维护,感谢
那些提出宝贵意见和打赏的网友,没有你们的支持,
网站不可能发展到今天,
继往开来,善终如始,我们将继续砥砺前行。
Copyright ©2014 iissy.com, All Rights Reserved.