Scheme是一种具有有限库的小型语言。本标准序曲提供了编程实践中使用的几个库:列表实用程序,列表推导式,模式匹配,结构,矩阵,哈希表,字典,输入/输出,,排序,高阶函数,数学函数,,随机数,控制流量,日期算术,单元测试、和杂项。此代码应直接适用于任何R5RS方案。在R6RS方案中,根据本地偏好,使用模块系统包装代码将非常有用。《标准前奏曲》会不时地改变,所以要经常回来看看。代码收集于http://programmingpraxis.codepad.org/hZnG9BCP.

列出实用程序

列表在Scheme中无处不在,因此拥有一组对列表进行操作的实用函数是很有用的。

采取返回一个新分配的列表,其中包含第一个n个列表的元素X轴; 如果X轴少于n个元素,返回所有元素。删除与…相反,返回列表的所有元素X轴除了第一个n个.拆分联合收割机:

(define(取n xs)
(let循环((n n)(xs-xs)(ys'()))
(如果(或(零?n)(空?xs))
(反向y)
(循环(-n 1)(cdr-xs)
(cons(car xs)ys)))

(定义(删除n个xs)
(let循环((n n)(xs-xs))
(如果(或(零?n)(空?xs))xs
(环路(-n 1)(cdr-xs)))

(定义(拆分n个xs)
(let循环((n n)(xs-xs)(zs'()))
(如果(或(零?n)(空?xs))
(值(反向zs)xs)
(环路(-n 1)(cdr-xs)(cons(car-xs)zs)))

休息一会儿,暂时、和分裂时期类似于,分裂,但它们不是计算元素,而是对元素的前缀进行操作x个属于X轴对于其中(提前?x个)不是-#如果:

(定义(take-while pred?xs)
(let循环((xs-xs)(ys'()))
(如果(或(null?xs)(不是(pred?(car xs)))
(反向ys)
(循环(cdr-xs)(cons(car-xs)ys))))

(定义(drop-while pred?xs)
(let循环((xs-xs))
(如果(或(null?xs)(不是(pred?(car xs)))xs
(循环(cdr-xs)))

(定义(split-while pred?xs)
(let循环((xs-xs)(ys'()))
(如果(或(null?xs)(不是(pred?(car xs)))
(值(反向为y)xs)
(环路(cdr-xs)(cons(car-xs)ys)))

缺点*类似于列表,但对最后一对的处理方式不同;(列表1 2 3)形成列表(1 2 3 . ()),但是(缺点*1 2 3)形成列表(1 2 . 3):

(定义(cons*first.rest)
(let循环((curr first)(rest rest))
(如果(null?rest)curr
(cons-curr(循环(轿厢休息)(cdr-rest)))

折叠使用用户特定的函数将值列表减少为单个值,是函数编程的基本习惯用法之一。左折叠式从左到右遍历列表X轴,应用二进制操作函数到基础和的第一个元素X轴,然后应用二进制操作函数的第一个结果操作函数和的第二个元素X轴等等,在每个步骤应用二进制操作函数转换为上一个结果操作函数和当前元素xs公司向右折叠工作方式相同,但从右向左。

(define(折叠左操作基xs)
(如果(null?xs)
底座
(左折叠式op(op base(car xs))(cdr xs)

(define(折叠右操作基xs)
(如果(null?xs)
底座
(op(car xs)(右折叠式op base(cdr xs)))

这个(范围[第一]过去的[])函数接受三个参数,并返回一个从开始的数字列表第一并在之前结束过去的,将每个数字递增.如果如果省略,则默认为1第一小于过去的,否则-1;如果第一也省略,默认为0。参数可以是任何数字类型。

(定义(范围.args)
(大小写(长度参数)
((1)(范围0(汽车参数)(如果(负?(汽车参数
((2)(范围(汽车参数)(cadr参数)(如果(=<=)))
(let循环((x(汽车参数))(xs'()))
(如果(le?(cadr args)x)
(反向xs)
(循环(+x(caddr args))(cons x xs)))
(else(错误的范围“无法识别的参数”))

映射端就像地图,但用追加而不是欺骗.重复重复计算函数如果相对于基本值英国标准,每次迭代后,通过删除第一个基值并在末尾附加新计算的值来移动剩余的基值,并返回第一个n个结果显示在列表中:

(define(mappend f.xss)(apply append(apply map f xss))

(定义(迭代n f.bs)
(let loop((n n)(b(car bs))(bs(cdr bs)))(xs'()))
(如果(零?n)(反向xs)
(let((new bs(append bs(list(apply f b bs)))))
(环路(-n 1)(新汽车-bs)(cdr新汽车-b)(cons b xs)))

例如,(定义(fib n)(迭代n+1 1))定义计算第一个n个斐波那契数

这个(过滤器pred? X轴)函数返回一个新分配的列表,其中只包含那些元素x个属于X轴对于其中(提前?x个)是真的。这个(删除x个 X轴)函数删除所有出现的x个从列表中X轴,使用相等吗?进行比较:

(定义(过滤器pred?xs)
(let循环((xs-xs)(ys'()))
(cond((null?xs)(反向为ys))
((pred?(汽车xs))
(循环(cdr-xs)(cons(car-xs)ys))
(其他(循环(cdr-xs)ys)))

(定义(删除x xs)
(let循环((xs-xs)(zs'()))
(cond((null?xs)(反向zs))
((等于?(car xs)x)(循环(cdr xs)zs))
(其他(环路(cdr-xs)(cons(car-xs)zs)))

压扁获取表示为带有子列表的列表的树,并返回仅包含树边缘元素的列表。这两棵树(a(b)c)((a)b)c)两个原因压扁返回(a b c):

(定义(展平xs)
(cond((null?xs)xs)
((对?xs)
(追加(展平(car xs))
(展平(cdr-xs))
(其他(列出xs))

全部?有吗?对列表的每个成员应用谓词。全部?收益#t吨如果(提前?x个)不是-#如果对于每个x个在输入列表中,或#如果否则。有吗?收益#如果如果(提前?x个)#如果对于每个x个在输入列表中,或#t吨否则。这两个函数都会尽快停止将谓词应用于输入as列表的元素。

(定义(所有?pred?xs)
(cond((空?xs)#t)
((pred?(汽车xs))
(全部?pred?(cdr-xs))
(其他#f))

(定义(任何?pred?xs)
(cond((null?xs)#f)
((pred?(汽车xs))#t)
(其他(任何“pred”(cdr-xs)))

拉链将多个列表转换为列表列表:

(define(zip.xss)(应用映射列表xss))

十字架生成一个或多个列表的叉积。该实现是一个功能性的珍珠由迈克·斯皮维(Mike Spivey)收集的克里斯托珀·斯特拉奇(Christoper Strachey)提供:

(定义(cross.xss)
(定义(f xs yss)
(定义(g x zss)
(定义(h ys uss)
(cons(cons x ys)uss)
(右折叠式hzss-yss)
(向右折叠g'()xs))
(向右折叠f(列表'())xss))

这个(制作清单n个 x个)函数返回一个新分配的列表,其中包含n个的副本x个:

(定义(品牌列表n x)
(let循环((n n)(xs'()))
(如果(零?n)xs
(回路(-n 1)(cons x xs)))

计算列表的总和通常很有用:

(定义(总和xs)(应用+xs))

最大值-依据就像内置的函数最大值,除了需要书信电报?比较函数作为要比较的项之前的第一个参数:

(定义(最大值-通过lt?.xs)
(let循环((xs(cdr-xs))(current-max(car xs)
(cond((null?xs)当前最大值)
((lt?current-max(汽车xs))
(环路(cdr-xs)(汽车xs))
(其他(回路(cdr-xs)电流-最大)))

列表推导

列表理解将高阶习惯用法映射、过滤和折叠成一种非常有用的语法糖形式,用于执行涉及列表的循环计算。《标准前奏曲》提供了三种类型的列表理解:

  • (列表快递 条款...)生成返回类型的对象的可能为空的列表快递
  • (总结快递 条款...)计算由快递
  • (第页,共页操作 基础 快递 条款...)就像总结,但使用用户定义的运算符和基替换的+和0总结

条款可能有四种类型:

  • (无功功率,无功功率范围[第一]过去的[])-绑定无功功率,无功功率第一,第一+,...,直到达到过去的,它不包含在输出中。如果第一未将其默认设置为0。如果未给定,如果(<第一 过去的)否则为-1。第一,过去的可以是任何数字类型;如果有第一,过去的输出列表的长度可能与(天花板(-(/(-过去的 第一))1).
  • (无功功率,无功功率在里面列表导出)-循环元素列表导出,按照从列表开始的顺序,依次将列表的每个元素绑定到无功功率,无功功率.
  • (无功功率,无功功率快递)-绑定无功功率,无功功率通过评估获得的值快递.
  • (pred? 快递)-仅在输出列表中包含这些元素x个对于其中(pred? x个)不是-#如果.

列表理解中绑定的变量范围是绑定子句右侧的子句(但不是绑定子句本身)加上结果表达式。当存在两个或多个生成器时,循环的处理方式就像它们从左到右嵌套一样;也就是说,最右边的生成器变化最快。作为退化情况,如果不存在生成器,则列表理解的结果是包含结果表达式的列表;因此,(列表1)生成仅包含元素1的列表。

列表理解由一个递归调用自身的宏展开,每个子句有一级递归,再加上基本用例的最后一级递归。完整的实现基于Kent Dybvig书中的set构造函数Scheme编程语言,如下所示:

(定义语法fold-of
(语法规则(范围为is)
((_“z”f b e)(设置!b(f b e
((_“z”f b e(v范围fst pst stp)c…)
(让*((x fst)(p pst)(s stp))
(le?(如果(正?s)=))
(do((v x(+v s)))((le?p v)b)
(折叠“z”f b e c…))
((_“z”f b e(v范围fst pst)c…)
(设*((x fst)(p pst)(s(如果(<x p)1-1)))
(折叠“z”f b e(v范围x p s)c…))
((_“z”f b e(v范围pst)c…)
(“z”f b e的倍数(v范围0 pst)c…)
((_“z”f b e(x in xs)c…)
(do((t-xs(cdr-t)))((null?t)b)
(出租(x(t车))
(折叠“z”f b e c…))
((_“z”f b e(x是y)c…)
(let((x y))(折叠“z”f b e c…))
((_“z”f b e p?c…)
(如果p?(折叠“z”f b e c…))
((_ f i e c…)
(let((b i))(“z”f b e c…)的折叠))

(define-syntax-list-of(syntax-rules())
((_ arg…)(背面(折叠
(λ(d a)(cons a d))'()参数…)))

(define-syntax-sum-of(syntax-rules())
((_ arg…)(+0 arg…的文件夹))

这里给出的列表理解用折叠理解。另一个扩展是第n个,它扩展列表理解并返回第n个项目:

(定义语法第n个
(语法规则()
((_ n expr子句…)
(let(第n个n))
(呼叫-无电流控制
(λ(返回)
(第页,共页
(λ(fst-snd)
(如果(零?第n个)(返回snd)
(开始(设置!第n个(第n个1))snd))
#f expr条款…))

模式匹配

模式匹配为析构函数列表、选择替代项和绑定变量提供了语法糖,并由列表比赛宏。语法(列表匹配快递 条款...)接受输入快递计算为列表的。条款s的形式(图案[挡泥板]快递),由图案匹配特定形状列表的,可选挡泥板如果模式要匹配,则必须成功,并且快递如果模式匹配,则对其进行评估。有四种类型的模式:

  • ()-匹配空列表。
  • (拍打0 拍打1...)-匹配长度正好等于拍打燕鸥元素。
  • (拍打0 拍打1... .拍打休息)-匹配长度至少与拍打字母点之前的tern元素。拍打休息是一个列表,其中包含输入列表中位于文字点之前的列表初始前缀之后的其余元素。
  • 拍打-匹配整个列表。应始终作为最后一句出现;出现在别处不是错误,但后面的子句永远不会匹配。

每个图案元素可以是:

  • 标识符-匹配任何列表元素。此外,列表元素的值被绑定到由标识符命名的变量,该标识符位于相应子句的挡泥板和表达式的范围内。单个模式中的每个标识符必须是唯一的。
  • 文字下划线-匹配任何列表元素,但不创建绑定。
  • 常量-匹配表达式是否等于常量值,但不创建绑定。
  • 引号表达式-如果表达式等于引号表达式,则匹配,但不创建绑定。
  • 准引号表达式-如果表达式等于准引号表达式,则匹配,但不创建绑定。

所有比较均与相等吗?.按照从左到右的顺序测试图案,直到找到匹配的图案;如果挡泥板存在,它必须评估为非-#如果为了比赛的成功。模式变量绑定在相应的挡泥板快递会话。找到匹配模式后,相应的快递session作为匹配的结果进行计算并返回。如果没有与输入列表匹配的模式,则会发出错误信号。

模式匹配由展开为康德每个模式包含一个子句的表达式;一个辅助宏处理各种类型的模式元素。基于Jos Koot思想的完整实现如下所示:

(定义语法列表匹配
(语法规则()
((_ expr(图案挡泥板…模板)…)
(let((obj expr))
(cond((list-match-aux obj模式挡泥板。。。
(列表模板)=>汽车)。。。
(其他(错误“list-match”模式故障))))

(定义语法列表-match-aux
(λ(stx)
(定义(下划线?x)
(和(标识符?x)(自由标识符=?x(语法_)))
(语法-case stx(准引号)
((_obj模式模板)
(语法(list-match-aux-obj模式#t模板))
((_obj()挡泥板模板)
(语法(和(null?obj)挡泥板模板))
((_obj下划线挡泥板模板)
(下划线?(语法下划线)
(语法(和挡泥板模板))
((_ obj var挡泥板模板)
(标识符?(语法变量))
(语法(let((var obj))(和fender模板)))
((_ obj(报价基准)挡泥板模板)
(语法(和(等于obj(引用数据))挡泥板模板))
((_ obj(准引用数据)挡泥板模板)
(语法(and(equal?obj(准引用数据))挡泥板模板))
((_ obj(kar.kdr)挡泥板模板)
(语法(and(pair?obj)
(let((kar-obj(car-obj))(kdr-obj(cdr-obj)))
(列表-match-aux kar-obj kar
(list-match-aux-kdr-obj-kdr挡泥板模板))))
((_对象常量挡泥板模板)
(语法(和(equal?obj const)fender template)))))

结构

R5RS方案没有提供任何创建新变量类型或将多个变量组合成一个单元的方法;R6RS提供执行这些任务的结构。以下结构与R6RS结构向上兼容。呼叫(定义结构名称 领域...)创建新结构。定义结构展开为构造函数(制造-名称 领域...),类型谓词(名称?对象)和访问器(名称-领域 x个)和设置器(套-名称-领域x个 价值)对于一个对象x个结构类型的名称.

(定义语法(定义结构x)
(定义(gen-id模板-id.args)
(数据->语法对象模板-id
(字符串->符号
(应用字符串附加
(映射(λ(x))
(if(字符串?x)x
(符号->字符串
(语法对象->基准x)))
args)))))
(语法-case x()
((_名称字段…)
(带语法
((构造函数(gen-id(语法名)“make-”(语法名称))
(谓词(gen-id(语法名)(语法名称)“?”)
((访问…)
(map(lambda(x)(gen-id x(语法名称)“-”x))
(语法(字段…))
((分配…)
(map(lambda(x)(gen-id x“set-”(语法名称)“-”x“!”))
(语法(字段…)))
(结构长度(+(长度(语法(字段…)))1))
((索引…)(设f((i 1)(id(语法(字段…))))
(如果(null?id)'()
(cons i(f(+i 1)(cdr ids))))
(语法(开始
(define(构造函数字段…)
(向量的名称字段…)
(define(谓词x)
(和(向量?x)
(=(向量长度x)结构长度)
(等式?(矢量参考x 0)'名称))
(定义(访问x)(矢量参考x索引)。。。
(define(赋值x更新)(向量集!x索引更新))
              ...))))))

矩阵

Scheme提供一维数组,称为向量,但没有二维数组。Kent Dybvig提供了矩阵定义为向量向量的数据结构Scheme编程语言:

(定义(make-matrix行-columns.value)
(do((m(make-vector行))(i 0(+i 1)))
((=i行)m)
(如果(null?值)
(向量集!mi(make-vector列))
(向量集!mi(make-vector列(车值)))

(定义(矩阵行x)(向量长度x))

(定义(矩阵cols x)(向量长度(向量ref x 0))

(定义(矩阵ref m i j)(向量ref(向量ref m i)j))

(定义(矩阵集!mi j x)(向量集!(向量参考mi)j x))

这个对于宏便于在矩阵的行和列上迭代(用于(无功功率,无功功率[第一]过去的[])身体...)首先将var绑定到,然后逐步迭代var,直到到达未绑定的过去;这个身体语句仅因其副作用而执行。步骤默认为1,如果第一小于过去的否则为-1;如果第一也未给定,默认为0:

(定义语法
(语法规则()
((对于(var first pass step)主体…)
(let((ge?(如果(=<=)))
(do((var first(+var step)))
((ge?var过去)
主体…))
((对于(var first pass)正文…)
(让*((f第一)(p过去)(s(如果(<第一过去)1-1)))
(对于(var f p s)主体…))
((用于(var过去)正文…)
(让*((p过去))(对于(var 0 p)主体…)))

散列表

哈希表是计算机科学最伟大的发明之一,允许快速检索键/值对。

手工制作创建哈希表的抽象数据类型的实例。手工制作采用四个参数:搞砸是一个获取密钥并返回整数的函数,该整数为存储密钥/值对的bucket提供地址。Eql?是接受两个键并返回的谓词#t吨如果它们是相同的并且#如果否则。哎呀如果表中不存在请求的键,则为哈希表返回的默认值。大小是存储键/值对的桶数;最好选择大小作为与键/值对的预期数量相似的数量级质数。生成散列返回一个函数,当使用适当的消息调用该函数时,该函数执行所请求的操作;对于由创建的哈希表

(定义状态标签(make-hash字符串散列字符串=?#f 4093))

适当的呼叫是

(状态标签'消息 参数...)

哪里消息参数可以是以下任一项:

  • 插入 钥匙 价值-插入一个钥匙/价值对,覆盖与钥匙
  • 查找 钥匙-检索与关联的值钥匙
  • 删除 钥匙-删除钥匙以及哈希表中的相关值(如果存在)
  • 更新 钥匙 程序 违约程序是一个以键和值作为参数并返回新值的函数;如果钥匙存在于哈希表中,更新电话程序使用钥匙及其关联的值,并存储程序替换原始值,否则更新在哈希表中插入一个新的键/值对键>和价值违约.
  • 登记-以列表形式返回哈希表中的所有键/值对

为一些消息提供了同义词;有关详细信息,请参阅源代码。

(定义(make-hash散列eql?oops大小)
(let((表(make-vector大小'()))
(lambda(消息.args)
(如果(eq?消息“登记”)
(let循环((k 0)(结果'()))
(如果(=尺寸k)
结果
(循环(+k 1)(追加(矢量参考表k)结果))
(let*((钥匙(汽车参数))
(索引(模(散列键)大小)
(bucket(向量引用表索引))
(案例信息
((查找获取引用调用)
(让环路((铲斗))
(秒((null?bucket)oops)
((eql?(caar桶)键)(cdar桶))
(其他(循环(cdr桶)))
((插入insert!ins-ins!set set!store store!install install!)
(向量集!表索引
(let循环(铲斗))
(cond((null?桶)
(列表(cons键(cadr参数))
((eql?(caar bucket)键)
(cons(cons键(cadr-args))(cdr-bucket))
(其他(cons(汽车桶)(loop(cdr桶))))
((删除-删除!del del!删除-删除!)
(向量集!表索引
(let循环(铲斗))
(cond((null?bucket)'())
((eql?(caar bucket)键)
(cdr存储桶)
(其他(cons(汽车桶)(loop(cdr桶))))
((更新更新!)
(向量集!表索引
(let循环(铲斗))
(cond((null?桶)
(列表(cons键(caddr参数))
((eql?(caar bucket)键)
(cons(cons键((cadr args)键(cdar存储桶)))(cdr存储桶)
(其他(cons(汽车桶)(loop(cdr桶))))
(其他(错误“哈希表”“无法识别的消息”))))

用于散列键的最常见数据类型是字符串,我们为其提供了以下散列函数:

(define(字符串散列字符串)
(let循环((cs(string->list-str))(s 0))
(如果(null?cs)s
(循环(cdr-cs)(+(*s 31))
(字符->整数(car cs))))

这个列表->哈希函数返回一个新分配的哈希表,其中包含输入列表中的每个键/值对,其中键是项的car,值是项的cdr:

散列“>(定义(列表->散列散列eql?oops大小xs)
(let((table(生成哈希值eql?oops大小)))
(do((xs-xs(cdr-xs))((null?xs)表)
(表格插入(caar xs)(cdar xs

辞典

标记-直径提供有序映射的抽象数据类型,有时称为字典。与只采用等式谓词的哈希表不同,字典采用小于谓词书信电报?这样它就可以按顺序迭代其键/值对。字典还提供了顺序统计信息,因此您可以按顺序选择第n个键/值对或查找给定键的序数秩。该实现使用avl树,因此对特定键/值对的任何访问都需要时间(日志n个). 字典由以下人员创建:

(make-dict-lt?)-返回一个新分配的空字典,该字典遵循less-than书信电报?排序谓词

字典由函数表示,一旦创建了字典,比如(定义dict(make-dict字符串<?)),运算符应用于消息传递样式,例如(字典'消息 参数 ...)哪里消息参数可以是以下任一项:

  • 空的?-收益#t吨如果字典是空的,否则#如果[零?]
  • 查找 钥匙&破折号;返回一个(钥匙.价值)对应于的对钥匙,或#如果如果钥匙字典中不存在[取来,得到]
  • 插入 钥匙 价值-返回一个新分配的字典,其中包括输入字典中的所有键/值对和输入钥匙价值,用匹配的密钥替换任何现有的密钥/值对;不允许使用重复密钥[商店,]
  • 更新 程序 钥匙 价值)-返回新分配的字典;如果钥匙字典中已存在,则与钥匙被替换为(程序 k个 v(v)),其中k个v(v)是现有的关键和价值;否则,输入键/值对将添加到字典中
  • 删除 钥匙-返回一个新分配的字典,其中钥匙不存在,无论它是否已经存在[去除]
  • 大小-返回字典中键/值对的数量[计数,长度]
  • 第n个 n个-返回n个字典中的第个键/值对,从零开始计数
  • 等级 钥匙-返回的序号位置钥匙在字典里,从零开始计数
  • 地图 程序)-返回一个新分配的字典,其中每个值替换为(程序 k个 v(v)),其中k个v(v)是现有的键和值
  • 折叠 程序 基础&mdsah;返回应用函数所累积的值(程序 k个 v(v) b条)到字典中的每个键/值对,累加基数的值b条每一步;按升序键访问对
  • 每个人 程序-仅评估其副作用(程序 k个 v(v))对于字典中的每个键/值对,按升序
  • 到列表-以升序返回字典中所有(key.value)对的新分配列表[登记]
  • 从列表中 X轴-返回一个新分配的字典,其中包括原始字典中的所有键/值对以及X轴; 任何键入X轴字典中已存在的,其值被中的相应值替换X轴
  • make-gen品牌-返回一个函数,每次调用该函数时,都会以升序返回字典中的下一个(key.value)对;当字典中的键/值对用完时,函数返回#如果每次调用时[发电机]

一些操作提供了同义词,如上面方括号中所示。

(定义(使dict lt?)

(定义语法定义生成器
(λ(x)
(语法-case x(lambda)
((stx名称(lambda formals e0 e1…))
(with-syntax((yield(datam->syntax-object(syntax-stx)'yield)))
(语法(定义名称
(lambda格式
(let((恢复#f)(返回#f))
(定义产量
(lambda参数
(呼叫-无电流控制
(λ(续)
(设置!继续(续)
(应用返回参数)))
(λ()
(呼叫-无电流控制
(λ(续)
(set!return续)
(第二条(简历)
(否则(让()e0 e1…)
(错误名称“意外返回”)))
((stx(name.formals)e0 e1…)
(语法(stx名称(lambda formals e0 e1…))))

(定义(树k v l r)
(向量k v l r(+(最大(ht l)(ht r))1)
(+(尺寸l)(尺寸r)1))
(定义(键t)(矢量参考t 0))
(定义(val t)(矢量参考t 1))
(定义(lkid t)(矢量参考t 2))
(定义(rkid t)(矢量参考t 3))
(定义(ht-t)(矢量参考t4))
(定义(大小t)(矢量参考t 5))
(定义(balt)(-(ht(lkid t))(ht(rkid t)))
(定义nil(矢量'nil'nil'nil'nil 0 0))
(定义(零?t)(等式?t零))

(定义(左旋转)
(如果(零?t)t
(树(键(rkid t))
(val(rkid t))
(树(键t)(val t)(lkid t)(lkid(rkid t))
(rkid(rkid t)))

(定义(旋转-右t)
(如果(零?t)t
(树(键(lkid t))
(val(lkid t))
(lkid(lkidt))
(树(键t)(val t)(rkid(lkid t)))

(定义(余额t)
(让(b(平衡))
(秒((<(abs b)2)t)
((正?b)
(如果(<-1(bal(lkid t)))(旋转-右t)
(旋转-右(树(键t)(val t))
(左转位(lkid t))(rkid t))
((负?b)
(如果(<(bal(rkid t))1)(左转位t)
(rot-left(树(键t)(val t))
(lkid t)(旋转右(rkid t))))

(定义(查找tk)
(条件((零?t)#f)
((lt?k(键t))(查找(lkid t)k))
((lt?(键t)k)(查找(rkid t)k
(其他(cons k(val t)))

(定义(插入t k v)
(cond((nil?t)(树k v nil nil))
((lt?k(键t))
(平衡(树(键t)(val t))
(插入(lkid t)k v)(rkid t))
((lt?(键t)k)
(平衡(树(键t)(val t))
(lkid t)(插入(rkid t)k v))
(其他(树k v(lkid t)(rkid t)))

(定义(更新t f k v)
(cond((nil?t)(树k v nil nil))
((lt?k(键t))
(平衡(树(键t)(值t)
(更新(lkid t)f k v)(rkid t)))
((lt?(键t)k)
(平衡(树(键t)(val t))
(lkid t)(更新(rkid t)f k v))
(其他(树k(f k(val t))(lkid t)(rkid t)))

(定义(删除后继t)
(如果(零?(lkid t))(值(rkid t)(键t)(val t))
(带值的调用
(lambda()(删除后继项(lkid t))
(λ(l k v)
(值(平衡(树(键t)(val t)l(rkid t))))

(定义(删除t k)
(条件(零?t)零)
((lt?k(键t))
(平衡(树(键t)(val t))
(删除(lkid t)k)(rkid t))
((lt?(键t)k)
(平衡(树(键t)(val t))
(lkid t)(删除(rkid t)k))
((零?(lkid t))(rkid t))
((零?(rkid t))(lkid t))
(else(带值的调用
(lambda()(删除后继项(rkid t))
(λ(r k v)(平衡(树k v(lkid t)r))))

(定义(nth t n)
(if(负?n)(错误'nth“必须为非负”)
(let(s(大小(lkid t)))
(秒(<n秒)(第n(lkid t)n))
((<s n)(第n个(rkid t)(-n s 1))
((零?t)#f)
(其他(cons(键t)(valt))))

(定义(秩tk)
(let循环(t t)(s(大小(lkid t)))
(条件((零?t)#f)
((lt?k(键t))
(环路(lkid t)(-s(大小(rkid(lkit)))1))
((lt?(键t)k)
(循环(rkid t)(+s(大小(lkid(rkid t)))1))
(其他s))

(定义(avl-map过程);(proc键值)
(如果(零?t)零
(树(键t)(proc(键t)(val t))
(avl-map程序(lkid t))
(avl-map程序(rkid t)))

(定义(avl-fold proc base t);(proc键值库)
(如果(零?t)基础
(avl-fold程序
(进程(密钥t)(值t)
(avl-fold程序基(lkid t))
(rkid t)))

(定义(avl-for-each过程);(proc键值)
(除非(零?t)
(每个进程的平均值(lkid t))
(程序(键t)(val t))
(每个进程的平均值(rkid t))

(定义(列表t)
(条件(无)(列表))
(和(零?(lkid t))
(列表(cons(键t)(val t))
(else(附加(到列表(lkid t))
(列表(cons(键t)(val t))
(列表(rkid t)))

(定义(从列表t xs)
(let循环((xs-xs)(t t))
(如果(null?xs)t
(循环(cdr-xs)(插入t(caar-xs)))

(定义生成器(make-gen t)
(平均值-每个(λ(k v)(产量(cons k v)))t)
(do()(#f)(产量#f))

(定义(新字典)
(lambda(message.args)(调度dict消息args))

(define(调度dict消息参数)
(定义(arity n)
(if(not(=(length args)n))(error‘dict“incorrect arity”))
(案例信息
((空?零?)(arity 0)(零?dict))
((查找获取)(arity 1)(应用查找字典参数)
((插入存储put)(arity 2)(新的(应用插入dict参数))
((更新)(arity 3)(新增(应用更新dict参数))
((删除删除)(arity 1)(新增(应用删除dict参数))
((大小计数长度)(arity 0)(大小字典))
(第n个)(arity 1)(应用第n个dict参数)
((秩)(arity 1)(应用秩dict参数)
((地图)(arity 1)(新(avl-map(car-args)dict))
((折叠)(arity 2)(avl折叠(汽车参数)(cadr参数)dict))
(每个(for-each)(arity 1)(每个(car args)dict)的平均值)
((到列表登记)(arity 0)(到列表目录)
((从列表)(arity 1)(新增(应用从列表dict参数))
((标记生成)(arity 0)(标记生成dict))
(else(错误'dict“无效消息”))

(向量集!nil 2 nil)(向量集:nil 3 nil)

输入/输出

读文件返回文件中的字符列表:

(定义(读文件名)
(带输入-from文件名(lambda())
(let循环((c(read-char))(cs'()))
(if(eof-object?c)(反转cs)
(循环(读-写)(cons-ccs))))

三个输入处理器用于每个输入,地图输入折叠输入以类似的方式操作输入文件或端口每个人,地图向左折叠对列表进行操作。这三个参数都有一个可选的最终参数。如果缺少最后一个参数,则从当前输入端口获取输入;作为一种副作用,端口上的所有字符都被耗尽了。如果最后一个参数是端口,则从端口获取输入,当函数返回时,端口保持打开状态;作为副作用,端口上的所有字符都已耗尽。如果最后一个参数是字符串,那么它将作为打开的文件名,用作输入源,并在函数返回之前关闭。对于所有三种功能,读者是一个从输入中返回下一项的函数,并且程序是对每个项目进行操作的函数。

(define(for-each-input读卡器进程.pof)
(让*((f?(和(pair?pof)(字符串?(car pof))))
(p(cond(f?(开放输入文件(车载pof)))
((对?pof)(汽车pof))
(其他(当前输入端口)))
(do((项目(阅读器p)(阅读机p)))
((eof对象?项)
(如果f?(闭合输入端口p))
(程序项))

(定义(地图输入读取器程序.pof)
(让*((f?(和(pair?pof)(字符串?(car pof))))
(p(cond(f?(开放输入文件(车载pof)))
((对?pof)(汽车pof))
(其他(当前输入端口)))
(let循环((项(读取器p))(结果'()))
(if(eof-object?item)
(开始(如果f?(闭合输入端口p))(结果相反))
(循环(读卡器p)(cons(进程项)结果))))

(define(fold-input读取器proc-base.pof)
(让*((f?(和(pair?pof)(字符串?(car pof))))
(p(cond(f?(开放输入文件(车载pof)))
((对?pof)(汽车pof))
(其他(当前输入端口)))
(let循环((项目(读取器p))(底座))
(if(eof-object?item)
(开始(如果f?(闭合输入端口p))基数)
(循环(读卡器p)(程序基本项))))

读取线从命名端口读取下一行文本,如果没有指定,则从当前输入端口读取。行是由换行符、回车符或两个字符按任意顺序终止的最大字符序列;文件中的最后一行无需终止。

(定义(readline.port)
(定义(吃p c)
(if(and(not)eof对象?(peek char p))
(char=?(peek char p)c))
(读-写p))
(let((p(if(null?端口)(当前输入端口)(汽车端口)))
(let循环((c(read-char p))(第'()行))
(cond((eof-object?c)(if(null?line)c(list->string(reverse line)))
((char=?#\newline c)(吃p#\return)(list->string(反向行))
((char=?#\return c)(吃p#\newline)(list->string(反向行))
(其他(回路(读-写p)(控制线路))))

输入过滤器是一个组合符,它接受一个reader函数和一个谓词,并返回一个只传递输入项的新reader函数x个对于其中(pred? x个)是非-#如果.

(定义(过滤器输入读取器pred?)
(lambda参数
(let循环((项(应用读取器参数)))
(if(或(eof-object?item)(pred-item))item
(循环(应用读取器参数))))

(弦indexc(c) 字符串)返回的第一个匹配项的从零开始的索引c(c)在里面字符串,或#如果如果c(c)不显示在中字符串:

(定义(字符串索引c str)
(let循环((ss(string->list-str))(k 0))
(cond((null?ss)#f)
((char=?(car ss)c)k)
(其他(环路(cdr-ss)(+k 1)))

案例转换由处理串下箱串接外壳:

(define(字符串小写字符串)
(列表->字符串
(映射字符小写
(字符串->列表字符串))

(define(字符串大写字符串)
(列表->字符串
(映射字符用例
(字符串->列表字符串))

字符串拆分获取一个分隔符和一个字符串,并返回以分隔符为界的子字符串列表。字符串连接则相反。

(定义(字符串分割sep str)
(define(f-cs-xs)(cons(list->string(reverse-cs))xs))
(let循环((ss(string->list-str))(cs'())(xs'()
(cond((null?ss)(反向(if(null吗cs)xs(f cs xs)))
((char=?(car ss)sep)(循环(cdr ss)'()(f cs xs))
(else(loop(cdr-ss)(cons(car-ss)cs)xs)))

(定义(字符串连接sep-ss)
(定义(f s ss)
(字符串附加s(字符串sep)ss))
(定义(连接ss)
(如果(null?(cdr-ss))(car-ss)
(f(car ss)(连接(cdr ss)))
(如果(null?ss)“”(连接ss))

弦长型返回的起始位置拍打燕鸥字符串ing,或#如果如果字符串ing不包含拍打燕鸥;它使用Knuth-Morris-Pratt字符串搜索算法:

(定义(字符串查找路径字符串)
(let*((plen(字符串长度pat))
(slen(字符串长度str))
(跳过(make-vector plen 0))
(let循环((i 1)(j 0))
(秒((=整数))
((char=?(string-ref pat i)(string-ref patj))
(向量集!跳过i(+j 1))
(循环(+i 1)(+j 1))
((<0 j)(循环i(矢量参考跳过(-j 1)))
(else(向量集!跳过i 0)
(循环(+i 1)j))
(让循环((p0)(s)(如果(null?s)0(cars)))
(秒(=秒)#f)
((char=?(字符串引用pat p)(字符串引用str s))
(如果(=p(-plen 1))
(-s plen-1)
(循环(+p1)(+s1)))
((<0 p)(循环(矢量参考跳过(-p 1))s))
(其他(环路p(+s 1))))

排序

大多数Scheme系统都提供排序功能,这在R6RS中是必需的分类,这是Kent Dybvig的书Scheme编程语言; 它稳定、实用、垃圾生成量小、速度快,并且还提供了合并功能。书信电报?是一个谓词,它包含的两个元素X轴和回报#t吨如果第一个在第二个之前,并且#如果否则:

(定义排序#f)
(定义合并#f)
(让()
(定义剂量
(λ(pred?ls n)
(如果(=n 1)
(列表(车辆ls))
(设(i(商n2))
(domerge pred?
(dosort pred?ls i)
(dosort pred?(list-tail ls i)(-n i))))
(定义domerge
(λ(pred?l1 l2)
(第二
((空?l1)l2)
((空?l2)l1)
((pred?(第二节车厢)(第十一节车厢))
(cons(第二节车厢)(domerge pred?l1(cdr l2))
(其他(cons(car l1)(domerge pred?(cdr l1)l2)))
(set!排序
(λ(pred?l)
(如果(null?l)l(dosort pred?l(length l)))
(设置!合并
(λ(预处理?l1 l2)
(domerge pred?l1 l2))

与unix版本一样,独特的返回删除了相邻重复项的输入列表。Uniq-c公司返回与相邻重复项计数成对出现的输入列表,就像unix一样uniq公司使用-c(c)标志。

(定义(唯一的eql?xs)
(cond((null?xs)'())
((空?(cdr-xs))xs)
((eql?(car xs)(cadr xs))
(唯一eql?(cdr-xs))
(其他(cons(car xs)(唯一eql?(cdr xs))))

(定义(uniq-c eql?xs)
(if(null?xs)xs
(let循环((xs(cdr-xs))(prev(car xs)(k 1)(result'()))
(cond((null?xs)(反向(cons(cons-prev k)结果))
((eql?(car xs)prev)(循环(cdr xs)prev(+k 1)结果)
(其他(环路(cdr-xs)(汽车xs)1(cons(cons-prev k)结果))))

矢量通过宾利/麦克罗伊进行分类快速排序.比较功能(cmp b条)当第一个参数小于、等于或大于第二个参数时,返回一个小于、等于或者大于零的整数。

(define(矢量排序!vec-comp)
(定义语法,同时
(语法规则()
((当pred?body…)
(do()(不是pred?)主体…)))
(定义语法赋值!
(语法规则()
((赋值!var-expr)
(开始(set!var-expr)var))

(定义len(矢量长度vec))
(定义-语法v(语法-规则()((vk)(矢量-参考vec k)))
(定义语法v!(语法规则()((v!k x)(向量集!vec k x)))
(定义语法cmp(语法规则()((cmp a b)(comp(v a)(v b))))
(定义语法lt?(语法规则()((lt?a b)(负?(cmp a b)))))
(定义语法交换!)语法规则()((交换!a b)
(让((t(v a))(v!a(v b))(v!b t)))
(定义(vecswap!a b s)
(do((a a(+a 1))(b b(+b 1)))((0?s))
(互换!a b))

(定义(med3 a b c)
(如果(lt?b c)
(如果(lt?b a)(如果(gt?c a)c a)b)
(如果(lt?ca)(如果(ltb?a)b a)c))
(定义(pv-init a n)
(let((pm(+a(商n 2)))
(当(>n 7)
(let((pla)(pn(+a n-1)))
(当(>n 40)
(设(s(商n8))
(集合!pl(med3 pl(+pls)(+pl s))
(设置!pm(med3(-pm s)pm(+pm s))
(设置!pn(med3(-pn秒)(-pn s)pn))
(设置!pm(med3 pl pm pn))
下午)

(让qsort((a 0)(n len))
(如果(<n 7)
(do((pm(+a1)(+pm1)))((不是(pla)(>(cmp(-pl1)pl)0)))
(交换!pl(-pl 1)))
(let((pv(pv-init an n))(r#f))
(pa a)(pba)(pc(+a n-1))(pd(+a n-1))
(交换!a pv)(设置!pv a)
(let循环()
(while(and(<=pb-pc)(=pc-pb)(>=(assign!r(cmp-pc-pv))0))
(当(=r 0)(交换!pc pd)(设置!pd(-pd 1)))
(设置!pc(-pc 1))
(除非(>pb-pc)
(交换!pb pc)(设置!pb(+pb 1))(集!pc(-pc 1)))
(let((pn(+an)))
(let(s(min(-pa-a)(-pb-pa)))(vecswap!a(-pb-s)s))
(设(s(最小值(-pd-pc)(-pn-pd 1)))(vecswap!pb(-pn-s)s))
(let((s(-pb-pa)))(当(>s1)(qsort a s))
(let((s(-pd-pc)))(当(>s1)(qsort(-pn-s)s))))))))

高阶函数

身份只返回它的参数,并且常数返回一个函数,该函数在调用时始终返回相同的值,而不管其参数如何。在编写高阶函数时,这两个函数都可用作递归基。

(定义(标识x)x)

(定义(常数x)(λys x))

返回它的第一个参数信噪比返回第二个参数;在编写函数字符串时,这两个函数偶尔都会用到:

(定义(fst x y)x)

(定义(snd x y)y)

函数组合通过一个接一个地部分应用多个函数来创建新函数。在最简单的情况下,只有两个函数,如果,组成如下((合成fg)x)≡(f(gx)); 可以绑定组合以创建新函数,如(定义fg(合成fg)).撰写采用一个或多个过程并返回一个新过程,该过程将执行与连续调用各个过程时相同的操作。

(定义(compose.fns)
(让comp((fns-fns))
(第二
((null?fns)'错误)
((空?(cdr-fns))(汽车fns)
(否则
(lambda参数
(带值的调用
(λ()
(适用
(比较(cdr-fns))
参数)
(汽车)))

补体接受一个谓词并返回一个新谓词#t吨原件寄回的地方#如果#如果其中原始返回非-#如果。它适用于以下功能滤波器花点时间将谓词作为自变量。

(define(补码f)(lambda xs(not(apply f xs)))

互换接受一个二进制函数并返回一个与原始函数类似但参数颠倒的新函数。当组合或使用以不方便的顺序接受参数的函数时,它很有用。

(定义(交换f)(λ(x y)(f y x))

节是部分应用于其某些参数的过程;例如,(双x)是乘法运算符对数字2的部分应用。部分有两种:左侧部分应用从左侧开始的参数,右侧部分应用从右侧开始的参数。左侧部分接受一个过程及其参数的一些前缀,并返回一个新过程,在该过程中部分应用了这些参数。权利-部分接受一个过程及其参数的某些反向后缀,并返回一个新过程,其中这些参数部分应用于该过程。

(define(左侧节进程参数)
(lambda xs(应用程序(附加参数xs))

(定义(右分区进程参数)
(lambda xs(应用程序(反向(追加(反向参数)(反向xs))))

Currying是一种重写带多个参数的函数的技术,这样它就可以被调用为一个函数链,每个函数都带一个参数;这项技术是以发现它的数学家哈斯克尔·库里的名字命名的(摩西·施芬克尔独立地发现了这项技术,但施芬克尔一词从未流行)。例如,如果div公司是除法运算符的curried形式,定义为(定义课程(div x y)(/x y)),然后发票是返回其参数的逆函数,定义为(定义inv(div 1)).

(定义语法curried-lambda
(语法规则()
((_()车身*…)
(开始正文*…)
((_(arg arg*…)主体*…)
(λ(arg)
(curried-lambda(参数*…)
车身*……))

(定义语法定义课程
(语法规则()
((_(函数参数…)正文*…)
(定义函数
(当前lambda(参数…)
身体*…))))

数学函数

供电功能(首次公开募股b条 e(电子))升高底座b条到整数次方e(电子)e(电子)必须为非负数:

(定义(ipow-be)
(如果(=e 0)1
(让循环(s b)(i e)(a 1));a*s^i=b^e
(设((a(if(奇?i)(*a s)a))(i(商i2)))
(如果(零?i)a(循环(*s)i a))))

正数的整数平方根是最大的整数,它与自身相乘时不超过给定的数字。整数平方根可以通过牛顿的导数近似法计算:

(定义(isqrt n)
(如果(不是(和(正?n)(整数?n))
(错误'isqrt“必须是正整数”)
(let循环((x n))
(设(y(商(+x(商nx))2))
(如果(<y x)(回路y)x)))

整数对数基数b条数字的n个是数字的倍数b条可以乘以自身而不超过n个:

(定义(ilog b n)
(让循环1((lo0)(b^lo1)(hi1)(b_hib))
(如果(<b^hin)(循环1 hi b^hi(*hi 2)(*b^hi-b^hi))
(让循环2((lo-lo)(b^lo-b^lo)(hi-hi)(b_hi-b^hi))
(如果(<=(-hi-lo)1)(如果(=b^hin)hi-lo)
(let*((mid(商(+lo hi)2))
(b^mid(*b^lo(出口b(-mid-lo)))
(cond((<n b^mid)(环路2 loo b^lo-mid-b^mid
((<b^midn)(循环2中间b^mid-hi-b^hi)
(其他中间))

模幂运算由函数提供(出口)b条 e(电子) )。这相当于(模(exptb条 e(电子)))除了该算法通过分阶段执行乘法和平方来避免大的中间幂的计算之外。

(定义(扩展)
(定义(m*xy)(模数(*xY)m))
(条件((零?e)1)
((偶数?e)(expm(m*b)(/e 2)m))
(其他(m*b(expm(m*bb)(/(-e 1)2)m)))

一半,双重的,广场,添加1,sub1(子1),日志2日志10非常方便,尤其是作为地图:

(定义(减半x)(/x 2))

(定义(双x)(+x x))

(定义(方形x)(*x x))

(定义(添加1 x)(+x 1))

(定义(子1 x)(-x 1))

(定义(log2 x)(/(log x)(log2))

(定义(log10 x)(/(log x)(log 10))

有时需要将数字的位数提取到不同的基数。以下是将数字转换为数字的函数,反之亦然:

(定义(数字n.args)
(let((b(if(null?args)10(car args)))
(让循环(n n)(d’()))
(如果(零?n)d
(循环(商n b)
(cons(模数n b)d)))

(定义(undigits ds.args)
(let((b(if(null?args)10(car args)))
(let循环((ds ds)(n 0))
(如果(null?ds)n
(环路(cdr-ds)(+(*n-b)(汽车ds))))

Common Lisp提供了一整套位操作符和位向量序列数据类型。该方案在其极简主义中,在R6RS需要一组最小的位操作符之前都没有提供,但仍然没有位向量。我们的套房很小,但很有用。请注意,所有位函数的效率都非常低;您应该使用Scheme实现提供的任何功能,而不是依赖下面给出的函数。

位运算符将数字视为二进制位的序列,并使用逻辑运算对其进行运算,同或,排他性或、和; 它们是使用基本算术实现的。算术移位乘以(正移位)或除以(负移位)两次幂:

(定义(log和a b)
(如果(或(零?a)(零?b))0
(+(*(原木(地板(/a 2)))(地板(/b 2))2)
(如果(或(偶数a)(偶数b))0 1))

(定义(逻辑x y)
(秒((=x y)x)
((零?x)y)
((零?y)x)
(否则
(+(*(logior(商x2)(商y2))2)
(如果(和(偶数x)(偶数y)))

(定义(logxor a b)
(条件((零?a)b)
((零?b)a)
(否则
(+(*(logxor(地板(/a2)))(地板(/b2))2)
(如果(即使?a)
(如果(偶数?b)0 1)
(如果(偶数?b)10)))

(定义(lognot a)(--1 a))

(定义(灰分cnt)
(如果(负?cnt)
(让(n(导出2(-cnt)))
(if(负?int)
(+-1(商(+1 int)n))
(商int n))
(*(expt 2 cnt)int))

位向量是使用字符数组实现的。它们被表示为一对,其中包含汽车中八位字符的矢量和cdr中位矢量的长度。生成比特向量创建一个请求长度的位向量,除可选参数外,所有位都为零val值是一个;注意确保位向量末尾的任何“slop”位都设置为零的逻辑,这在位向量计数功能:

(定义(make-bitvector len.val)
(设(v)生成向量
(天花板(/len 8))
(如果(和(配对?val)(=(车辆val)1)255 0))
(如果(and(pair?val)(=(car val)1)(not(zero?(modulo len 8)))
(do((i8(-i1)))((=i(模数长度8)))
(向量集!v(floor(/len 8))
(logand(矢量参考v(floor(/len 8)))(lognot(ash 1(-i 1))))
(cons v len))

位矢量参考返回的值bv[idx],零或一:

(定义(位向量ref bv idx)
(如果(<-1 idx(cdr-bv))
(let((指数(商idx 8))(偏移量(模idx 8
(如果(奇数?(灰分(矢量参考(汽车bv)索引)(偏移量))1 0))
(错误“bitvector-ref“超出范围”))

位向量集!将请求的位设置为1。位向量重置!将请求的位设置为零:

(定义(位向量集!bv-idx)
(如果(<-1 idx(cdr-bv))
(let((指数(商idx 8))(偏移量(模idx 8
(向量集!(car bv)索引
(逻辑器(矢量参考(汽车bv)索引)(灰1偏移))
(错误“bitvector-set!”超出范围“))

(定义(位向量重置!bv-idx)
(如果(<-1 idx(cdr-bv))
(let((指数(商idx 8))(偏移量(模idx 8
(向量集!(car bv)索引
(logand(矢量参考(汽车bv)指数)(lognot(灰1偏移)))
(错误“bitvector-reset!”超出范围“))

位向量计数返回输入位矢量中的一位数。每个字节的计数在计数矢量。注意最后一个字节不是特殊的,因为生成位矢量小心地确保任何“slop”位为零:

(定义(位向量计数bv)
(让*(计数#(
          0 1 1 2 1 2 2 3 1 2 2 3 2 3 3 4 1 2 2 3 2 3 3 4 2 3 3 4 3 4 4 5
          1 2 2 3 2 3 3 4 2 3 3 4 3 4 4 5 2 3 3 4 3 4 4 5 3 4 4 5 4 5 5 6
          1 2 2 3 2 3 3 4 2 3 3 4 3 4 4 5 2 3 3 4 3 4 4 5 3 4 4 5 4 5 5 6
          2 3 3 4 3 4 4 5 3 4 4 5 4 5 5 6 3 4 4 5 4 5 5 6 4 5 5 6 5 6 6 7
          1 2 2 3 2 3 3 4 2 3 3 4 3 4 4 5 2 3 3 4 3 4 4 5 3 4 4 5 4 5 5 6
          2 3 3 4 3 4 4 5 3 4 4 5 4 5 5 6 3 4 4 5 4 5 5 6 4 5 5 6 5 6 6 7
          2 3 3 4 3 4 4 5 3 4 4 5 4 5 5 6 3 4 4 5 4 5 5 6 4 5 5 6 5 6 6 7
          3 4 4 5 4 5 5 6 4 5 5 6 5 6 6 7 4 5 5 6 5 6 6 7 5 6 6 7 6 7 7 8))
(len(cdr-bv))(索引(商len 8))(偏移量(模len 8))
(do((i 0(+i 1)))
(计数0(+count(矢量参考计数(矢量参考(汽车bv)i))))
((=指数i)计数))

位向量长度返回位向量中的位数:

(定义(位向量长度bv)(cdr-bv))

随机数

这是在斯坦福图形库作者:Donald E.Knuth。基于滞后fibonacci生成器(Knuth称之为减法)n个= (n个-24负极n个-55)模块,其中是偶数(我们取= 231)和数字0通过54不是所有值都是偶数,它提供从0到2的范围内的值31不包括,期限至少为255−1,但有理由推测周期为285− 230对于所有种子值,但至多只能选择一个,生成的数字的低阶位与高阶位一样随机。您可以阅读Knuth的原始版本的随机数生成器,网址为http://tex.loria.fr/sgb/gb_flip.pdf; 另请参阅我们的练习GB_FLIP公司。此随机数生成器适用于模拟,但不安全。

调用时没有参数,(兰特)返回一个精确的有理数,其范围从零到一。使用单个数字参数调用,(兰特种子)重置随机数生成器的种子;最好是种子是一个大整数(8到10位数字-日期的格式为YYYYMMDD),尽管像0.3(2^35的十分之三)和-42(2^35-42)这样的种子也可以工作。由于洗牌算法需要自己的数据存储,知道当前种子不足以重新启动生成器。因此,(兰特)返回发电机的完整当前状态,以及(兰特设置状态)将生成器重置为给定状态,给定状态为(兰特).(randint n)返回一个小于n的随机非负整数,(randiint first-passit)返回介于first-inclusive和past-exclusive之间的随机整数。

两个版本的mod-diff型包括功能。如果您的方案提供原木本机版本,否则为通用版本。请注意原木有时以不同的名称提供;例如,它是按位和R6RS中。

(定义rand#f)
(定义randint#f)
(let((two31#x80000000)(a(make-vector 56-1))(fptr#f))
(定义(mod-diff x y)(模(-x y)two31));通用版本
  ; (定义(mod-diff x y)(logand(-x y)#x7FFFFFFF));快速版本
(定义(翻转循环)
(do((ii 1(+ii 1))(jj 32(+jj 1)))((<55 jj))
(向量集!a ii(mod-diff(向量引用a ii)(向量引用ajj))
(do((ii 25(+ii 1))(jj 1(+jj 1)))((<55 ii))
(向量集!a ii(mod-diff(向量引用a ii)(向量引用ajj))
(设置!fptr 54)(矢量参考a 55)
(定义(init-rand种子)
(let*((种子(mod-diff种子0))(prev种子)(next 1))
(矢量集!a 55 prev)
(do((i 21(模(+i 21)55))((零?i))
(vector-set!a i next)(set!next(mod-diff prev next))
(set!seed(+(商种子2)(if(奇数种子)#x4000000))
(set!next(mod-diff下一种子))(set!prev(vector-ref a i))
(翻转)(翻转)
(定义(下一个区域)
(如果(负?(矢量参考fptr))(触发循环)
(let((next(vector-ref a fptr)))(set!fptr(-fptr 1))next))
(定义(统一土地m)
(let(t(-two31(模two31 m)))
(let循环((r(下一个兰特)))
(如果(列出a))
((eq?(car seed)'集合)(集合!fptr(caadr seed))
(集合!a(列表->向量(cdadr种子))
(else(/(init-rand(模(分子
(不精确->精确(汽车种子))two31))two 31)))
(set!randint(lambda参数
(cond((空?(cdr参数))
(如果(<(car args)two31)(统一地(car ags))
(地板(*(下一个地面)(汽车参数)))
((<(car args)(cadr args))
(let((span(-(cadr args)(car args))))
(+(汽车参数)
(如果(<span two31)(单边跨度)
(地板(*(相邻)跨度))))
(其他(let((span(-(car args)(cadr args))))
(-(汽车参数)
(如果(<span two31)(单边跨度)
(地板(*(相邻)跨度))))

财富从列表中随机选择一项;第一项以概率1/1选择,第二项以概率1/2替换选择,第三项以概率1/3替换选择,依此类推,以便k个第个以概率1选择项目/k个该名称源自同名的unix游戏,该游戏从每行包含一个警句的文件中随机选择一个警语。

(定义(财富x)
(let循环((n1)(x#f)(xs-xs))
(cond((空?xs)x)
((<(兰特)(/n))
(循环(+n1)(car xs)(cdr xs))
(其他(循环(+n 1)x(cdr-xs)))

收件人洗牌列表,将其转换为向量,使用Knuth算法对向量进行洗牌,然后将结果转换回列表:

(定义(洗牌x)
(do((v(列表->向量x))(n(长度x)(-n 1)))
((零?n)(向量->列表v))
(让*((r(randint n))(t(矢量参考vr)))
(向量集!v r(向量参考v(-n 1))
(向量集!v(-n 1)t))

控制流程

而不是单臂写作如果s、 通常最好使用什么时候除非,它准确地说明了它是什么。什么时候?除非由许多方案系统本地提供;对于那些没有的,它们如下所示:

(定义语法
(语法规则()
((当pred?expr…时)
(如果是pred?(开始表达…))

(定义语法,除非
(语法规则()
(除非预先表达…)
(如果(不是pred?)(开始表达…)))

While期间只要控制谓词为true,就执行代码块:

(定义语法,同时
(语法规则()
((当pred?body…)
(do()(不是pred?)主体…)))

这个带值调用语法使使用多个值变得困难。这个let值R6RS所需的语法以及许多R5RS系统中存在的语法要方便得多。下面给出了一个从Kent Dybvig偷来的实现,用于那些缺少let值:

(定义语法let值
(语法规则()
((_()f1 f2…)(设()f1 f2…)
((_((fmls1表达式1)(fmls2表达式2)…)f1 f2…)
(let-values-help fmls1()()expr1((fmls2-expr2)…)(f1 f2…))

(定义语法let-values-help
(语法规则()
((_(x1.fmls)(x…)(t…)e m b)
(let-values-help fmls(x…x1)(t…tmp)e m b))
((_()(x…)(t…)e m b)
(带值的调用
(λ()e)
(λ(t…)
(let值m(let(x t)…)。b) )))
(_ xr(x…)(t…)e m b)
(带值的调用
(λ()e)
(λ(t….tmpr)
(let值m(let(x t)。。。(xr tmpr))。b) ))

生成器提供了一种易于使用的语法,用于将值的产生与使用分离开来,并且在许多其他语言中以本机方式提供(有时称为迭代器)。由定义的函数定义生成器创建一个函数,该函数在调用时返回序列中的下一个值。例如:

>(定义生成器(产量123)
(产量1)(产量2)(产量3)
>(定义y(产量123))
>(年)
1
>(年)
2
>(年)

>(年)
收益率例外123:意外回报

定义生成器如下所示:

(定义语法定义生成器
(λ(x)
(语法大小写x(lambda)
((stx名称(lambda形式为e0 e1…))
(with-syntax((yield(datum->syntax(syntax-stx)'yield)))
(语法(定义名称
(lambda格式
(let((恢复#f)(返回#f))
(定义产量
(lambda参数
(呼叫-无电流控制
(λ(续)
(设置!继续(续)
(应用返回参数)))
(lambda()
(呼叫-无电流控制
(λ(续)
(set!return续)
(第二条(简历)
(否则(让()e0 e1…)
(错误名称“意外返回”)))
((stx(name.formals)e0 e1…)
(语法(stx名称(lambda formals e0 e1…))))

日期算术

天文学家计算朱利安日期的数字,即自公元前4713年1月1日以来经过的天数。教皇格里高利十三世于1582年2月24日颁布的公历是世界上许多地方使用的民间日历。功能朱利安格雷戈里亚人在两个日历之间转换;必须指定为完整的四位数(除非您希望年份在第一个千年中),范围从1月的1到12月的12,白天范围从1到31,一周中的哪一天可以计算为模7的朱利安数,其中0表示星期一,6表示星期日:

(定义(朱利安年月日)
(让*(a(商(-14个月)12))
(年(+4800年(-a))
(m(+月(*12a)-3))
(+天
(商(+(*153m)2)5)
(*365年)
(商y 4)
(-(商y 100))
(商y 400)
       (- 32045))))

(定义(格雷戈里安·朱利安)
(让*((j(+julian 32044))
(g(商j 146097))
(dg(模数j 146097))
(c(商(*(+(商dg 36524)1)3)4))
(直流(-dg(*c 36524))
(b(商dc 1461))
(db(模数dc 1461))
(a(商(*(+(商db 365)1)3)4))
(da(-db(*a 365))
(y(+(*g 400)(*c 100)(*b 4)a))
(m(-(商(+(*da 5)308)153)2))
(d(+da(-(商(*(+m4)153)5))122)
(年份(+y(-4800)(商(+m2)12))
(月(+(模(+m 2)12)1))
(天(+d 1))
(值年-月-日))

几个世纪以来,复活节日期的计算,被称为计算机是全世界最重要的科学事业。功能复活节计算给定年份复活节日期的朱利安数。如果抵消是复活节前后的天数;例如,要计算狂欢节的日期,请给出一个抵消第页,共47页:

(定义(复活节年偏移量)
(让*((a(模年19))
(b(商年份100)
(c(模年100)
(d(商b4))
(e(模数b4))
(f(商(+b 8)25)
(g(商(+(-b f)1)3))
(h(模量(-(+(*19a)b 15)d g)30))
(i(商c4))
(k(模c 4))
(l(模(-(+32(*2e)(*2i))h k)7))
(m(商(+a(*11小时)(*22升))451)
(月(商(-(+h l 114)(*7 m))31)
(天(+(模数(-(+h l 114)(*7 m))31)1))
(q(如果(对偏移)(轿厢偏移)0))
(+(朱利安年月日)q))

计算当前日期需要本地Scheme解释器的帮助,因为Scheme没有定义日期的标准函数。下面显示了两个流行的Scheme口译员的版本:

(定义(今天);Chez方案
(朱利安
(日期-年份(当前日期))
(+(日期-月(当前日期))1)
(日期(当前日期))

(定义(今天);Mz方案
(let((今天(秒->日期(当前秒)))
(朱利安(日期-年份-今天)(日期-月份-今天)

单元测试

这个断言宏在测试程序时很有用。语法(断言快递 结果)计算快递结果; 如果它们相同,则assert不生成输出,也不返回值。但如果表达式和结果不同,断言编写包含以下内容的消息快递以及计算两者的结果快递结果.断言是宏,而不是函数,因为它打印文本快递作为其输出的一部分,可以很容易地在一长串断言中知道哪个错误。断言根据“没有消息就是好消息”的理论,如果一切顺利,就不会产生任何输出

(定义语法断言
(语法规则()
((断言表达式结果)
(如果(不是(等于?expr结果))
(对于每个显示器`(
#\newline“断言失败:”#\newline
expr#\newline“应为:”,result
#\newline“returned:”,expr#\newline))

其他

定义不可集成类似于定义用于过程定义,但过程的代码为集成的(有些人会说内联)无论何时调用该过程,都可以消除与该过程相关的函数全部开销。用定义的任何程序可定义积分必须出现在源代码中对定义的标识符的第一个引用之前。保留了词法范围,在调用时扩展定义过程主体中的宏,在适当的时间对集成过程的实际参数进行一次评估,可积过程可以用作一级值,递归过程不会导致无限递归扩展。定义不可集成见R.Kent Dybvig书的第8.4节Scheme编程语言:

(定义语法(定义可积x)
(定义(make-residual-name名称)
(数据->语法对象名称
(字符串->符号
(字符串追加“剩余-”
(符号->字符串(语法对象->数据名称))))
(语法-case x(lambda)
((_(name.args))。阀体)
(语法(定义可积名称(lambda args.body)))
((_名称(lambda formals form1 form2…))
(标识符?(语法名称))
(带语法((xname(make-residual-name(语法名称))))
(语法
(开始
(定义语法(名称x)
(语法-case x()
(_(标识符?x)(语法xname))
((_参数(……))
(语法
((流体-et-语法
((名称(标识符-语法xname))
(lambda形式form1形式2…)
arg(…)))))
(定义xname
(fluid-let-syntax(名称(identifier-syntax-xname))
(lambda formals form1 form2…)))

Scheme提供了健康的宏(尽管句法case提供安全弯曲卫生的方法);相比之下,Common Lisp提供了不卫生的宏。在某些情况下,不卫生的宏比卫生的宏更方便;保罗·格雷厄姆在他的书中提供了许多例子在Lisp上.定义宏为Scheme提供了不卫生的宏:

(定义语法(定义宏x)
(语法-case x()
((_(name.args))。阀体)
(语法(定义宏名称(lambda args.body))
((_变压器名称)
(语法
(定义语法(名称y)
(语法-case y()
((_.args)
(数据->语法对象
(语法_)
(使用变压器
(语法对象->数据(语法参数))))

以下示例改编自Graham的书:

(define-macro(when test.body)`(cond(,test.,body)))

(定义宏观(aif测试形式然后是其他形式)
`(let((it,test-form))
(如果是,则填写表格))

(定义宏(当pred?.body时)
`(aif,pred?(begin,@body))

当宏破坏了卫生性时,有时生成一个唯一的符号很有用,它可以用作变量名或以其他方式使用。这里是生成符号程序:

(定义同义词
(设((n-1))
(λ()
(设置!n(+n 1))
(字符串->符号
(字符串-追加“gensym-”
(数字->字符串n)))

框提供了一种通过引用将参数传递给过程的方法,而不是通常的传递值;将参数放入框中,然后在过程中访问并重置参数:

(定义(方框v)(矢量v))
(define(unbox框)(矢量参考框0))
(define(box!box v)(矢量集!box 0 v))

5对《标准前奏曲》的回应

  1. 编程实践

    这个伊洛格函数以前是

    (定义(ilog b n)
    (如果(零?n)-1
    (+(ilog b(商n b))1))

    为了支持这个新版本,该函数已被弃用

    (定义(ilog b n)
    (让循环1((lo0)(b^lo1)(hi1)(b_hib))
    (如果(<b^hin)(循环1 hi b^hi(*hi 2)(*b^hi-b^hi))
    (让循环2((lo-lo)(b^lo-b^lo)(hi-hi)(b_hi-b^hi))
    (如果(<=(-hi-lo)1)(如果(=b^hin)hi-lo)
    (let*((mid(商(+lo hi)2))
    (b^mid(*b^lo(出口b(-mid-lo)))
    (cond((<n b^mid)(环路2 loo b^lo-mid-b^mid
    ((<b^midn)(循环2中间b^mid-hi-b^hi)
    (其他中间))

    原来是O(n),替换为O(log n)。回路1计算二进制搜索的初始边界,由执行回路2.

  2. 编程实践

    这个兰特兰丁先前给出的函数如下所示;这些版本现在被弃用,取而代之的是Donald Knuth在斯坦福图形库:

    (定义兰特
    (出租*((a 3141592653)(c 2718281829)
    (m(出口235))(x 5772156649)
    (下一个(λ()
    (let((x-prime(模(+(*a x)c)m))
    (集合!x x素数)
    (k 103)
    (v(列表->矢量(反向
    (let循环((ik)(vs(list x)))
    (如果(=i 1)vs
    (循环(-i 1)(cons(next)vs))))
    (y(下一个))
    (初始化(λ)
    (集合!x s)(向量集合!v 0 x)
    (do((i 1(+i 1)))((=i k))
    (矢量设置!vi(下一步)))
    (λ种子
    (cond((null?种子)
    (设*((j(商(*ky)m))
    (q(矢量参考v j))
    (设置!y q)
    (向量集!v j(下一个))(/y m))
    (((eq?(car seed)'get)(列出c m x y k v))
    ((等式?(汽车种子))
    (let((state(cadr seed)))
    (set!a(list-ref状态0))
    (set!c(list-ref状态1))
    (set!m(list-ref状态2))
    (set!x(list-ref状态3))
    (设置!y(list-ref状态4))
    (set!k(list-ref状态5))
    (set!v(list-ref状态6)))
    (else(init(模(分子
    (不精确->精确(汽车种子))m))
    (兰特))

    (define(randint.args)
    (cond((null?(cdr参数))
    (地板(*(兰特)(汽车参数)))
    ((<(car args)(cadr args))
    (+(地板(*(兰特)(-(cadr args)(汽车参数))))
    (其他(+(上限(*(rand)(-(cadr args)(car args))))

  3. 编程实践

    的原始版本isqrt公司如下图所示,已被亨利·科恩(Henri Cohen)的一个版本所取代(参见他的教科书中的算法1.7.1计算代数数论课程)它更漂亮、更快、产生的垃圾更少:

    (定义(isqrt n)
    (让循环((xn)(y(商(+n1)2)))
    (如果(<=0(-y x)1)x
    (回路y(商(+y(商n y))2)))

  4. 编程实践

    《标准前奏曲》曾经包含一个生成列表排列的函数。该函数已被删除,因为它对于通用标准前奏曲来说过于具体。原始功能及其描述如下:

    有时,生成列表排列的列表很有用。以下函数来自Shmuel Zaks,一种生成排列的新算法,技术报告220,以色列理工学院,1981年:

    (定义(排列xs)
    (定义(rev xs n ys)
    (如果(零?n)年
    (版本(cdr-xs)(-n 1)(缺点(汽车xs)ys))
    (let((xs-xs)(权限(列表xs)))
    (定义(perm n)
    (如果(>n 1)
    (do((j(-n 1)(-j 1)))
    ((零?j)(perm(-n 1))
    (允许(-n 1))
    (set!xs(rev xs n(list-tail xs n))
    (set!perms(cons xs perms)))
    (perm(长度xs))
    烫发)

  5. 编程实践

    的原始版本首次公开募股如下所示,不是尾递归;它已被一个适当的尾部递归版本取代:

    (定义(ipow-be)
    (条件((零?e)1)
    ((偶数?e)(ipow(*b)(/e 2))
    (其他(*b(ipow(*b b)(/(-e 1)2))))