文档章节

Scheme语言入门

平凡之路
 平凡之路
发布于 2014/11/27 15:21
字数 2845
阅读 3647
收藏 7

Scheme语言入门

最早听说 LISP,是 Stallman 的 GNU Emacs 中将 LISP 作为嵌入语言,定制和增强 Emacs。GNU Emacs 是一个文本编辑器,文本就是一种符号,而 Lisp 正好就是针对符号计算发明的,因此在GNU Emacs 中使用 Lisp 是顺理成章的事情。

Lisp 语言的历史已经很久了,几乎与 Fortran 一样长。二十世纪五十年代,计算机科学家先是发明了针对数字计算的 Fortran 语言,后来针对符号计算,由MIT 的John McCarthy于1960年开发出了Lisp(List processing)语言。该语言原来是为表处理而设计的编程语言,后来广泛用于处理人工智能问题。Lisp 程序中充满了一对对嵌套的小括号,这些嵌套的符号表达式体现着递归。递归是数学上的基本概念之一,从递归理论出发,一切可以计算的函数最终都可以划归为几种基本的递归函数的种种组合。

1994年时众多 Lisp 版本又得到了相当的统一,统一之后的版本称为Common LISP。Common Lisp 含有非常丰富的库,仅仅语言的规范就长达千页以上,包括面向对象的 CLOS。

Scheme 语言是 Lisp 的一个现代变种、方言,诞生于1975年,由 MIT 的 Gerald J. Sussman and Guy L. Steele Jr. 完成。Scheme语言的规范很短,总共只有50页,甚至连Common Lisp 规范的索引的长度都不到,但是却被称为是现代编程语言王国的皇后。它与以前和以后的 Lisp 实现版本都存在一些差异,但是却易学易用。

DSSSL需要完成的工作是解析文档,它的设计就采用了Scheme语言。本书时介绍DocBook的专著,因此并不打算写一个Scheme大全,只是想通过蜻蜓点水的介绍使读者认识Scheme,能够达到看懂和简单的修改DSSSL。

Scheme特点

Scheme语言具有它独特的魅力,看看Scheme的语法特点:

  • 括号嵌套

    Lisp 程序中充满了一对对嵌套的小括号,这些嵌套的符号体现了最基本的数学思想——递归。

  • 语法简洁

    Scheme语言的规范很短,总共只有50页。

  • 函数编程语言

    一个函数(Function)是这个编程语言中所谓的第一等的公民。也就是说函式可以像一个 int 或者 float 一样被很方便的传递来传递去。这也就是所谓"Functional 编程语言"中,Functional 一词的由来。

  • 自动内存管理

    自动内存管理可不是JAVA的专利呦。

  • 可移植性好

    Scheme开发的程序有很好的可移植性,这是由于Scheme是一种解释语言,在不同的平台都可以有相应的解释器。

  • 适合于作为脚本语言和嵌入语言

    语法简洁,这使得Scheme的实现可以非常的经济,一个Scheme解释器可以非常的小巧。Scheme可以作为脚本语言而内嵌于一些工具之中,如:GNU Emacs。

其他特点还有,关键字对大小写不敏感。

数据结构

  • 数字

    下面都是合法的数字表示方法:47,1/3,2.3,4.3e14,1+3i。

  • 字符

    字符前面需要用#\做前缀。如下面都是合法字符:

    #\a #\A #\b #\B #\space #\newline

  • 字符串

    由双引号括起来的字符组成字符串。如:"A little string"

  • 布尔值

    布尔值True和False分别用 #t 和 #f 表示。

  • 列表

    用圆括号括起来的,可以包含任何数据类型的称为列表。如: (a little (list of) (lists))

  • 数组(vector)

    用#为前缀,如: #(1 2 "string" #\x 5)

  • 函数(或称为过程)

    把函数作为一种数据类型,是Scheme语言的特色。

  • 符号

    符号除了不能够以数字开头的任何字符可组成符号。如:Symbols: this-is-a-symbol foo a32 c$23*4&7+3-is-a-symbol-too!

表达式和函数

注释

分号开始一段注释。如:

(+ 3 1) ;return 4

 

常量表达式

  1. 常量表达式

    常量表达式返回本身的值。如:

    3.14 ; 返回 3.14 #t ; 返回布尔值 #t #\c ; 返回字符 #\c "Hi!" ; 返回字符串 "Hi!"
  2. 引用(Quotation)

    语法:(quote obj)  或者简写为 'obj

    (+ 2 3)   ; 返回 5 '(+ 2 3)  ; 返回列表 (+ 2 3) (quote (+ 2 3)) ; 返回列表 (+ 2 3)

表达式记法

Scheme的表达式的写法有些特别,表达式用括号括起来。括号里面的第一个出线的是函数名或者操作符,其它是参数。Scheme的这种表达式写法可以叫做前置式。下面是一些Scheme的表达式的例子以及其对应的C语言的写法。

  Scheme                               C ------------------------------------------------------------------ (+ 2 3 4)                       (2 + 3 + 4)  (< low x high)                  ((low < x) && (x < high))  (+ (* 2 3)                      (* 4 5)) ((2 * 3) + (4 * 5))  (f x y)                         f(x, y)  (define (sq x) (* x x))         int sq(int x) { return (x * x) }

 

赋值和函数定义

  1. let 表达式和赋值

    语法:(let ((var val) ...) exp1 exp2 ...)

    说明:let 表达式的赋值只在表达式内部有效。

    示例:

    (let ((x 2) (y 3)) (+ x y))

    ; 先赋值: x=2, y=3,再计算x+y的值,结果为5。注意 (x 2) 和 (y 3) 外还有一层括号。

    更多的示例:

    (let ((f +))   (f 2 3))  ; return 5  (let ((f +) (x 2))   (f x 3))  ; return 5  (let ((f +) (x 2) (y 3))   (f x y))  ; return 5
  2. 用define 和 set! 赋值

    语法:(define var exp)  , (set! var exp)

    说明:define和 set! 表达式的赋值在全局有效。define 和 set! 的区别是define既能赋值又能定义变量,而set!只能对已经定义的变量赋值。

    示例:

    (define a 1)   a ; return 1 (set! a 2) a ; return 2 (let ((a 3)) a)  ; return 3 a ; return 2 (let ((a 3)) (set! a 4) a) ; return 4 a ; return 2 (let ((a 3)) (define a 5) a) ; return 5 a ; return 2 (set! b 1) ; 错误,b尚未定义
  3. lambda 表达式和函数定义

    语法:(lambda (var ...) exp1 exp2 ...)

    说明:lambda 表达式用于定义函数。var ... 是参数,exp1 exp2 ...是函数的执行 部分。通常需要结合局部定义 let 或者全局定义表达式 define,再进行函数调用。

    示例:

    ((lambda (x) (+ x x)) (* 3 4))  ; return 24

    说明:先用lambda定义了函数,参数是x,函数返回x+x。同时该语句也完成了函数调用,实参是 12 (等于3*4),因此返回值是 24 (等于12+12)。

  4. 在let表达式中定义函数。

    Scheme语言中,函数作为一种数据类型,通过赋值语句,将lambda表达式赋值给相应的函数。

    示例:

    (let ((double (lambda (x) (+ x x))))   (list (double (* 3 4))         (double (/ 99 11))         (double (- 2 7))))     ; return (24 18 -10)

    说明:let表达式将lambda定义的函数赋值给double,参数是x,返回 x+x。接下来分别三次调用 double 函数,并将结果以列表形式返回。list 表达式负责生成列表。

  5. 用define全局定义表达式来定义函数。

    用 let 定义的函数只能在 let 表达式中有效,如果想定义在整个程序中有效的函数定义,需要用到全局定义表达式——define。

    示例:

    (define double (lambda (x) (+ x x))) (double 12)            ; return 24 (double (* 3 4))       ; return 24

    说明:define表达式定义了全局有效的函数 double。两次调用double的返回值都是 24。

  6. 定义函数的简写

    用 define 定义的函数的语法可以简化,即将 lambda 去掉。即将语法

    (define var0   (lambda (var1 ... varn)     e1 e2 ...))

    简写为:

    (define (var0 var1 ... varn)   e1 e2 ...)

    示例:

    (define (double x) (+ x x)) (double 12)            ; return 24 (double (* 3 4))       ; return 24

    说明:本例是前一个例子的简化版本。更简介,明了。

顺序计算表达式

语法:(begin exp1 exp2 ...)

说明:顺序执行表达式 exp1, exp2, ...,返回最后一个表达式的结果

示例:

(define x 3) (begin   (set! x (+ x 1))   (+ x x))             ; 返回结果 8

 

说明:begin 表达式,依次先用set!表达式为x赋值为4,在运算x+x,返回结果8。

条件表达式

  1. 关系运算符

    (< -1 0)   #t (> -1 0)   #f  (eqv? 'a 'a)  #t
  2. 逻辑运算

    (not #t)   #f (not #f)  #t  (not 1)   #f (not '(a b c))   #f  (or)   #f (or #f)   #f (or #f #t)   #t (or #f 'a #f)   a  (and)   #t (and #f)   #f (and #f #t)   #f (and #f 'a #f)   #f (and 'a #t 'b) 'b
  3. if 表达式

    语法:(if test consequent alternative)

    说明:如果test表达式为真,返回 consequent,否则返回 alternative。

    示例:

    (define (abs n)     (if (< n 0)         (- 0 n)         n))

    说明:函数abs功能为取绝对值。

  4. cond 表达式

    语法:(cond (test exp) ... (else exp))

    说明:多路分支判断表达式,类似于C语言的 "if ... else if ... else"。

    示例:

    (define abs   (lambda (n)     (cond       ((= n 0) 0)       ((< n 0) (- 0 n))       (else n))))

    说明:用cond表达式重新实现取绝对值函数 abs。

  5. case 表达式

    语法:(case exp0 clause1 clause2 ... )

    clause 的语法结构为:((key1 ...) exp1 ...) 最后一个表达式的结构可以为:(else exp1 exp2 ...)

    说明:类似于C语言的 "switch ... case..." 语句。

    示例:

    (let ((x 4) (y 5))   (case (+ x y)     ((1 3 5 7 9) 'odd)     ((0 2 4 6 8) 'even)     (else 'out-of-range))) ; 返回 odd

    说明:case 表达式先计算 x+y 的值为9,接下来在key中进行匹配,并返回对应的表达式的值 'odd。

循环

  1. do 表达式

    语法:(do ((var1 val1 update1) ...) (test res ...) exp ...)

    说明:类似于C语言的for循环。先将val1赋值给var1,...,之后循环开始,在每次循环的开始,先执行表达式 test,如果返回布尔值真,则循环终止,并返回结果 res,如果表达式 test返回布尔值#f,则运行表达式 exp...,之后依次用 update1 ... 的值来为变量 var1 ... 重新赋值。

    示例1:计算阶乘 n! = n*(n-1)!

    (define factorial   (lambda (n)     (do ((i n (- i 1)) (a 1 (* a i)))         ((zero? i) a))))  (factorial 10)  ; 返回 3628800

    说明:其对应的C语言实现如下

    long factorial(int n) { int  i=n; long a=1; for (i=n;; i--) { if (i == 0) return a; a *= i; } }

    示例2:计算fibonacci数列:f(n+1)=f(n)+f(n-1) , n>0, f(1)=1, f(0)=0

    (define fibonacci   (lambda (n)     (if (= n 0)         0         (do ((i n (- i 1)) (a1 1 (+ a1 a2)) (a2 0 a1))             ((= i 1) a1)))))  (fibonacci 6)  ; 返回 8

    说明:其对应的C语言实现如下

    long fibonacci(int n) { long f=1; int i = n; int a1= 1; int a2= 0; if (n == 0) return 0; while(1) { if (i == 1) return a1; i--; a1=a1+a2; a2=a1; } }
  2. map 表达式

    语法:(map procedure list1 list2 ...)

    说明:列表 list1 list2 ... 必须具有同样的长度;过程 procedure 接受的参数个数同列表的个数,各个列表中对应的变量分别作为过程 procedure 的参数被执行, 将每次的运算结果以列表形式返回。

    (map abs '(1 -2 3 -4 5 -6))  ; 返回 (1 2 3 4 5 6),abs接受一个参数 (map (lambda (x y) (* x y))      '(1 2 3 4)      '(8 7 6 5)) 返回(8 14 18 20) ,lambda (x y) 接收两个参数
  3. for-each 表达式

    语法:(for-each procedure list1 list2 ...)

    说明:同 map表达式, 但是不返回结果列表。

     

本文转载自:http://www.worldhello.net/doc/docbook_howto/ar01s04s06.html

共有 人打赏支持
上一篇: 时区问题
平凡之路
粉丝 42
博文 54
码字总数 14528
作品 0
海淀
架构师
私信 提问
免费的编程中文书籍索引【收藏速度】

语言无关类 优质博客 PyTab在线手册中心 ImportNew 廖雪峰的官方网站 程序员博客墙 操作系统 开源世界旅行手册 鸟哥的Linux私房菜 Linux 系统高级编程 The Linux Command Line (中英文版) L...

yonghu86
2015/04/15
0
0
Fanta/free-programming-books-zh_CN

免费的编程中文书籍索引 免费的编程中文书籍索引,欢迎投稿。 国外程序员在 stackoverflow 推荐的程序员必读书籍,中文版。 stackoverflow 上的程序员应该阅读的非编程类书籍有哪些? 中文版...

Fanta
2016/11/14
0
0
总有你要的编程书单(GitHub )

目录 IDE IntelliJ IDEA 简体中文专题教程 MySQL 21分钟MySQL入门教程 MySQL索引背后的数据结构及算法原理 NoSQL Disque 使用教程 Neo4j .rb 中文資源 Redis 命令参考 Redis 设计与实现 The ...

汇智网
2017/11/22
0
0
free-programming-books-zh.md

语言无关MySQL NoSQL PostgreSQL Web WEB服务器 其它 函数式概念 分布式系统 在线教育 大数据 操作系统 数据库 智能系统 正则表达式 版本控制 程序员杂谈 管理和监控 编程艺术 编译原理 编辑...

银月光海
2016/05/20
70
0
开源电子书

目录 语言无关类 操作系统 智能系统 分布式系统 编译原理 函数式概念 计算机图形学 WEB服务器 版本控制 编辑器 NoSQL PostgreSQL MySQL 管理和监控 项目相关 设计模式 Web 大数据 编程艺术 ...

zting科技
2017/12/11
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Typora快捷键

无序列表:输入-之后输入空格 有序列表:输入数字+“.”之后输入空格 任务列表:-[空格]空格 文字 标题:ctrl+数字 表格:ctrl+t 生成目录:[TOC]按回车 选中一整行:ctrl+l 选中单词:ctrl+...

AzureMonkey
47分钟前
2
0
SpringBoot2.x配置Cors跨域

1 跨域的理解 跨域是指:浏览器A从服务器B获取的静态资源,包括Html、Css、Js,然后在Js中通过Ajax访问C服务器的静态资源或请求。即:浏览器A从B服务器拿的资源,资源中想访问服务器C的资源。...

hengbao5
今天
2
0
mybatis(7) - 分页

一般程序在处理sql分页的场景,要么选择在程序中对所有的结果集sublist,要么在写sql时指定limit。那如何利用mybatis的特性在处理分页呢? 分页插件 适用于数据量大的情况下。 在真正执行sql...

noob_fly
今天
5
0
SpringBoot之使用jpa/hibernate

Springboot版本是2.1.3.RELEASE 1、依赖 List-1.1 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId></dependenc......

克虏伯
今天
6
0
安卓手机如何快速投屏到windows(10/8.1/7)电脑上

前提: 手机和电脑连接的网络必须在同一局域网下。 优势: 手机和电脑不需要下载对应平台的应用,完全使用全系统自带功能。 附加: 以下演示是安卓手机和windows操作系统电脑,并且win10和win10...

皇冠小丑
今天
21
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部