【Scheme】
Scheme是一种函数式编程语言，是Lisp的两种主要方言之一，不同于与之并列的Common Lisp，Scheme遵循极简主义哲学，以一个小型语言核心作为标准，加上各种强力语言工具（语法糖）来扩展语言本身。Scheme是第一个使用静态作用域的Lisp方言，也是第一个引入头等续体和“干净宏”的编程语言。
==简介==
在1975年，麻省理工学院的杰拉德·杰伊·萨斯曼与盖伊·史提尔二世，开发出了Scheme语言最初版本，随后两人通过发表「λ论文集」而不断对它进行完善和推广。Scheme与λ演算关系十分密切，故将小写字母「λ」用作标志。
麻省理工学院与其他一些院校，曾采用Scheme教授计算机科学入门课程。著名的入门教材《-{zh-hans:计算机程序的构造和解释;zh-hant:电脑程式的构造和解释}-》（SICP），利用Scheme来诠释程序设计。Scheme有众多实现可视为一个主要优势，然而不同实现之间的差异成为了它的一个劣势，Scheme掌控委员会声称，它是“世上最不可移植的编程语言”，并且是一个“编程语言家族”而非一个单一的语言。
==历史==
===起源===
Gerald Jay Sussman
Scheme起源于1958年由约翰·麦卡锡提出的Lisp语言。麦卡锡通过Lisp证明了，经由几个简单的算子，与用作匿名函数的借鉴自阿隆佐·邱奇的λ表示法，就可以构建出图灵完备的系统。麦卡锡提出的S-表达式，可以将程序与数据用相同的结构存储，这被称为同像性。Scheme的语法即来自S-表达式，这一特性使得在Scheme中实现自循环直译器变得非常简单。
在1973年，麻省理工学院的Carl Hewitt提出的一种叫做演员模型的计算模型，并用Lisp开发当时叫做Planner-73的新语言来实现它dead-url=no。杰拉德·萨斯曼与盖伊·史提尔为了理解演员模型，决定在Maclisp工作环境中实现一个微型Lisp解释器，并接着增加创建演员和发送消息的机制。两人很快发现了演员模型与λ演算之间的相似性，所谓「演员」就是彼得·兰丁提出的闭包，而Joel Moses在1970年已将它介入Lisp用来解决funarg problem；故而实现演员的关键，是将词法作用域介入到Lisp中。
在1975年，基于对λ演算表达能力的理解• First, we realized that all the patterns of control structure that Hewitt had described in terms of actors could equally well be described by the λ-calculus. ……• Second, we realized that the λ-calculus — a small, simple formalism — could serve as the core of a powerful and expressive programming language. (Lisp had adopted the λ-notation for functions but had failed to support the appropriate behavior for free variables. The original theoretical core of Lisp was recursion equations, not the λ-calculus.) ……• Third, we realized that in our quest for the “ultimate AI language” we had come full circle. As the MIT school had struggled to find more and more powerful ways to express and manipulate control structure to support heuristic search, we had progressed from Lisp to CONVERT to Planner to Conniver to PLASMA to a simple variation of Lisp!|access-date=2021-11-07|archive-date=2022-04-06|archive-url=https://web.archive.org/web/20220406235711/https://www.researchgate.net/publication/227202780_The_First_Report_on_Scheme_Revisited|dead-url=no，杰拉德·萨斯曼与盖伊·史提尔开发出了新型Lisp解释器dead-url=no，并将在其上完成的微缩版的演员实现命名为「Schemer」，后因ITS作业系统文件名的字符数限制而改为Scheme。尽管Hewitt指出了Scheme不能表达特定类型的原语演员，Scheme解释器本身采用的简约的语法和语义，很快赢得广泛接受。
在1978年，盖伊·史提尔二世和杰拉德·杰伊·萨斯曼发表了《修订的Scheme报告：一种LISP方言》，从而完善了Scheme的功能，并正式将其确立为Lisp的一种主要方言。在1998年，二人在总结Scheme历史时指出，简单而强大的λ演算，使得Scheme最终得以实现极度的精简化。
===λ论文集===
「λ论文集」是杰拉德·萨斯曼与盖伊·史提尔撰写的关于Scheme的一系列论文，最早作为麻省理工学院的内部备忘录发表。通常认定λ论文集包括：
* 1975年: Scheme: An Interpreter for Extended Lambda Calculus.
* 1976年: Lambda: The Ultimate Imperative.
* 1976年: Lambda: The Ultimate Declarative.
* 1977年: Debunking the 'Expensive Procedure Call' Myth, or, Procedure Call Implementations Considered Harmful, or, Lambda: The Ultimate GOTO.
* 1978年: The Art of the Interpreter or, the Modularity Complex (Parts Zero, One, and Two).
* 1978年: RABBIT: A Compiler for SCHEME.dead-url=no
* 1979年: Design of LISP-based Processors, or SCHEME: A Dialect of LISP, or Finite Memories Considered Harmful, or LAMBDA: The Ultimate Opcode.
* 1980年: Compiler Optimization Based on Viewing LAMBDA as RENAME + GOTO. AI: An MIT Perspective.
* 1980年: Design of a Lisp-based Processor. CACM. 23. 11.
===标准化===
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-smalldead-url=no，它为教学场合提供对IEEE/R5RS标准的及时更新，R7RS-small目前已经有了一些实现支持；而另一个是作为标准库合集的R7RS-LargeHowever, Scheme is used for practical programming, and there a minimal language is not a help. Additional data structures and programming idioms make the programmer’s job easier (if they are well-designed!). Therefore, as part of the R7RS design process, it was agreed that a subsequent report would appear on “R7RS-Large”, a collection of Scheme libraries that are useful across a wide variety of problems.The decision of what was to appear in R7RS-Large was left to the community, and a strategy was adopted: proposals were published as SRFIs (Scheme Requests For Implementation, http://srfi.schemers.org), and the community would then vote on which would be adopted.As a result, R7RS-Large will be developed in several stages, depending upon the support from the community for each proposal. At each stage, several SRFIs are frozen, and implementers and users may then rely upon those libraries being in the final product. (In a few cases, a library might be revisited, with a subsequent stage adopting a completely backward-compatible new version of that library; this has already happened with generators.)When this process has completed, the resulting interim reports will be collated and reorganized into a final document.|access-date=2022-11-13|archive-date=2022-11-13|archive-url=https://web.archive.org/web/20221113052556/https://gitlab.com/vmanis/r7rs-large/-/tree/master/reports|dead-url=no，它包含了各种提议被接纳并进入冻结状态的Scheme Requests for Implementation（SRFI），以此为实际编程场合提供对R6RS标准的继续完善，Larceny和Chibi提供了R7RS-Large过渡草案红色版（即数据结构部份）的全部SRFI的实现。
==命名法==
在正式场合比如Scheme标准的Nomenclature中，提及一个λ表达式或原始过程时，偏好使用词语“过程”而非“函数”。在一般使用中，词语“过程”和“函数”是可互换使用的。过程应用有时被正式称呼为“组合”（combination）。
同其他Lisp一样，在Scheme中使用术语“thunk”，来提及没有实际参数的过程。术语“适当”（proper）尾递归，称谓所有Scheme实现的一个性质，它们都进行尾递归优化，从而支持无限数目的活跃尾递归。
==基础特征==
Scheme大体上是一个函数式程式语言，并支援其他编程范型。它同其他Lisp编程语言家族语言共享了很多特征。它的非常简单的语法基于Lisp的S-表达式：即圆括号包围的列表，其中的前缀是算符，而随后那些的是实际参数。故而Scheme程序由嵌套的列表的序列构成。列表也是Scheme中的主要数据结构，这导致了在源代码和数据格式之间的紧密等价性，即同像性。Scheme程序可以轻易的动态建立和求值Scheme代码片段。
Scheme与其他Lisp方言的列表，都是基于最基础的数据结构有序对（pair）。Scheme从它的Lisp先辈继承了一组丰富的列表处理原始运算，比如cons、car和cdr。Scheme的变数都使用动态强型别系统，Scheme中的过程都是头等物件，即头等函数，因此过程可以作为值赋值给变量，或作为实际参数传递给过程。
本章主要集中于语言的创新特征，它们使得Scheme区别于其他Lisp方言。除非专门指出，这里描述的特征都有关于R5RS标准。在本章例子中使用“===> 结果”表示法，来指示求值在紧前行上的表达式的结果。这同于R5RS中使用的约定。本章节描述的特征使得Scheme不同于在它之前的其他编程语言。Scheme的这些方面强烈的影响了Scheme语言的所有产品，并共通于始自1975年最初的λ论文，特别是自从R2RS以来，所有版本的Scheme编程语言。
===极简主义===
Scheme的简约性，使它成为具备同级别功能的程式语言中最易于实现的语言，这得益于使用λ演算来从更原始的形式导出多数的语言语法。例如在R5RS标准中，定义了23个基于S-表达式的语法构造，其中14个被归类为派生形式或库形式，它们可以被写为涉及原则上为lambdad的更基础形式的宏。正如R5RS（§3.1）所说：“最基础的变量绑定构造是lambda表达式，因为所有其他的变量绑定构造，都可以依据lambda表达式来做出解释”。
基础形式：define、lambda、quote、if、define-syntax、let-syntax、letrec-syntax、syntax-rules、set!
派生形式：do、let、let*、letrec、cond、case、and、or、begin、命名let、delay、unquote、unquote-splicing、quasiquote
下面是一个宏的例子，它将let实现为使用lambda来进行变量绑定的一个表达式：
(define-syntax let
(syntax-rules ()
((let ((var expr) ...) body ...)
((lambda (var ...) body ...) expr ...))))
使用上述定义的let，一个Scheme实现可以将(let ((a 1)(b 2)) (+ b a))，重写为((lambda (a b) (+ b a)) 1 2)，这将实现任务缩减为编码过程实例化的工作。
===词法作用域===
不像更早期的Lisp比如Maclisp，Scheme像多数现代语言一样，采用了词法作用域：在一个程序单元中所有可能的变量绑定，都可以通过阅读这个程序单元来分析出来，而不需要考虑到它可能在其中被调用的那些上下文。这对比于动态作用域，它是早期Lisp方言的特征，因为在当时的编译器和解释器中，用来实现词法作用域算法的原始的文字替换方法，关联着处理代价。在动态作用域的Lisp中，对一个过程内的自由变量的引用，依赖于这个调用的上下文，完全有可能引用到这个过程外部的相当不同的绑定。
===λ演算===
邱奇的λ演算的数学表示法，启发了Lisp使用lambda作为介入一个过程的关键字，并影响了Lisp中涉及到使用高阶函数的函数式编程技术的发展。但是早期的Lisp由于对自由变量的处理方式
，而不适合表达λ演算。
一个正式的λ系统，拥有一些公理和一个完备的推理规则。这有助于使用数理逻辑和工具来进行分析。在这个系统中，演算可以被看作直接的演绎。λ演算的语法，来自用x, y, z, ...、圆括号、空格、点号和符号λ形成的递归表达式。λ演算的功能包括：首先，充当强力的数理逻辑的起点。其次，它可以缩减编程者在考虑实现细节上的要求，因为它可以用于模拟机器求值。最后，λ演算建立了一个坚实的元理论。
在Scheme中，lambda关键字被用于定义匿名过程，并且使用define基础形式来定义命名过程，即将一个lambda过程绑定到指名的全局变量cite web|title=The Revised Report on SCHEME: A Dialect of LISP|author=Gerald J. Sussman, Guy L. Steele Jr.|url=https://dspace.mit.edu/handle/1721.1/6283|year=1978|quote=Atoms which are not atomic symbols (identifiers) evaluate to themselves. Typical examples of such atoms are numbers, arrays, and strings (character arrays). Symbols are treated as identifiers or variables. They may be lexically bound by lambda-expressions. There is a global environment containing values initially have as their values primitive operations such as, for example, CAR, CONS, and PLUS. SCHEME differs from most LISP systems in that the atom CAR is not itself an operation (in the sense of being an invocable object, e.g. a valid first argument to APPLY), but only has one as a value when considered as an identifier.|access-date=2021-11-07|archive-date=2022-04-06|archive-url=https://web.archive.org/web/20220406235427/https://dspace.mit.edu/handle/1721.1/6283。在Scheme中，(define (foo x) (+ x 1))与(define foo (lambda (x) (+ x 1)))
，在语法上是等同的。例如有序对可以表示为：
(define (cons x y)
(lambda (m) (m x y)))
(define (car z)
(z (lambda (p q) p)))
(define (cdr z)
(z (lambda (p q) q)))
这样定义出来的cons、car和cdr，满足恒等式(car (cons x y))等于x，和(cdr (cons x y))等于y。甚至递归也可以表示为利用λ演算的Y组合子。
词法作用域的介入，通过在某些形式的λ表示法，和在工作编程语言中它们的实际表达之间做出等价，解决了早期Lisp的有关问题。Sussman和Steele展示了，通过将λ表达式用作“控制结构和环境修改器”，而非简单的过程实例化，可以用这个新语言优雅的导出其他编程语言包括ALGOL和Fortran的，所有指令式和声明式语义，和其他Lisp的动态作用域。他们在第一篇λ论文中，与对Scheme的首次描述一起，介入了continuation-passing style，并在后续的论文中，他们推进演示了在这种实际使用中体现出的λ演算的原生能力。
===过程应用中的求值次序===
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)))
===> 3
ev是一个过程，它描述传递给它的实际参数，接着返回这个实际参数的值。在调用过程+来加1和2之中，表达式(ev +)、(ev 1)和(ev 2)可以按任何次序求值，只要效果上它们不像是并行求值的。因此在上述例子被执行的时候，下列三行可以被标准Scheme按任何次序来显示，然而一行的文本不可以同另一行的文本产生交错，因为这会违反顺序求值约束。
Evaluating 1
Evaluating 2
Evaluating procedure
===块结构===
Scheme的代码块结构，不同于早先Lisp的语言构造
。自从R2RS开始，Scheme中的块，是通过三个“绑定构造”来实现的：let、let*和letrec。例如，下列构造建立一个块，其中叫做var的符号被绑定到数10：
(define var "goose")
; 这里任何到var的引用都会被绑定到"goose"
(let ((var 10))
;; 语句走到这里时，任何到var的引用都会绑定到10
)
; 这里任何到var的引用都会绑定到"goose"
依据编程者的需要，块可以嵌套来建立任意复杂的块结构。使用块结构来建立局部绑定，减轻了不然可能发生的命名空间冲突。
let的变体之一let*，允许绑定引用在前面相同构造中定义的变量，例如：
(let  ((var1 10)
(var2 (+ var1 12)))
;; 但是var1的定义不可以引用var2
)
let的另一个变体letrec，被设计用来确使互递归过程可绑定彼此。
下面的例子是侯世达雌雄序列：
; 将侯世达雌雄序列计算为有序对的列表
(hofstadter-male-female 8)
===> ((1 . 0) (1 . 0) (2 . 1) (2 . 2) (3 . 2) (3 . 3) (4 . 4) (5 . 4) (5 . 5))
在一个单一的letrec中的所有过程，可以通过名字引用其他的过程，还有在相同的letrec中此前定义的变量的值，但是它们不可以引用在相同的letrec中此后定义的变量的值。
let的变体“命名let”形式，在let关键字之后有一个标识符。它将这些let变量绑定到一个过程的实际参数，这个过程的名字由这个标识符给出，它的过程体是let形式的主体。这个过程体可以通过调用这个过程达成想要的重复。命名let被广泛用于实现迭代。
一个简单的计数器例子:
(let loop ((n 1))
(if (> n 10)
'()
(cons n
(loop (+ n 1)))))
===> (1 2 3 4 5 6 7 8 9 10)
就像Scheme中的任何过程一样，在命名let中建立的这个过程是头等对象。
===适当尾递归===
Scheme拥有一个迭代构造do，但是在Scheme中更推崇的惯用法，是使用尾递归来表达迭代。遵循标准的Scheme实现被要求优化尾递归，从而支持无限数量的活跃尾递归（R5RS sec. 3.5），这个性质被Scheme报告描述为“适当”（proper）尾递归，它使得Scheme编程者可以安全的使用递归结构来书写迭代算法，这在很多时候是更符合直觉的。尾递归过程和命名let形式，提供对使用尾递归的迭代的支持。
; 建造从0到9的平方的列表：
; 注意：loop简单的就是用作标签的一个任意符号。任何符号都行。
(define (list-of-squares n)
(let loop ((i n) (res '()))
(if ( (0 1 4 9 16 25 36 49 64 81)
===头等续体===
在Scheme中续体是头等对象。自从R2RS开始，Scheme提供了过程call-with-current-continuation
（简写为call/cc），通过把当前续体包装成脱离（escape）过程来捕获它，并把它绑定到编程者提供的一个过程的一个形式参数（R5RS sec. 6.4）。头等续体使得编程者能够建立非局部控制结构比如迭代、协程和回溯。
续体可以被用来模拟指令式编程语言中return语句的行为。下列函数find-first接受函数func和列表lst，返回lst中的第一个元素x，它使得(func x)返回真。
(define (find-first func lst)
(call-with-current-continuation
(lambda (return-immediately)
(for-each
(lambda (x)
(if (func x)
(return-immediately x)))
lst)
#f)))
(find-first integer? '(1/2 3/4 5.6 7 8/9 10 11))
===> 7
(find-first zero? '(1 2 3 4))
===> #f
David Madore在1999年提出的阴阳谜题，展示了Scheme可以将续体当作头等对象处理，绑定它们到变量，和把它们作为给过程的实际参数来传递。
===统一的命名空间===
对比于Common Lisp，在Scheme中所有的数据和过程共享一个共同的命名空间，而Common Lisp中有函数和数据分离的命名空间，使得一个函数和一个变量可以有相同的名字，并且将一个函数作为值引用时要求特殊的表示法。这有时叫做“Lisp1与Lisp2”差异，二者分别称谓Scheme的统一的命名空间，和Common Lisp的分立的命名空间。
在Scheme中， 可以使用操纵和绑定数据的原始运算来绑定过程。没有等价于Common Lisp的defun和#'的原始运算。
; 变量绑定到一个数：
(define f 10)
f
===> 10
; 变化（改变绑定值）
(set! f (+ f f 6))
f
===> 26
; 将一个过程赋值给相同的变量：
(set! f (lambda (n) (+ n 12)))
(f 6)
===> 18
; 将一个表达式的结果赋值给相同的变量：
(set! f (f 1))
f
===> 13
; 函数式编程：
(apply + '(1 2 3 4 5 6))
===> 21
(set! f (lambda (n) (+ n 100)))
(map f '(1 2 3))
===> (101 102 103)
==实现标准==
本章归档多年来做出的给与Scheme特定特征的设计决定，它们不是最初设计的直接产物。
=