Scheme
命令行
在终端执行
mit-scheme打开scheme的命令行。编写
test.scm文件,在命令行中加载:(load "test.scm")
表达式
基本使用
1 ]=> (+ 1 2)
;Value: 3- 一对括号代表了一次计算的步骤,只输入
+ 1 2会直接返回这三个元素; - 左括号后紧跟的是函数名,后面是参数;
- 标记的分隔符是空格、制表符或者换行符,逗号和分号不是分隔符;
- 函数
+可以接受任意多的参数。
四则运算
+、-、*、/都接受任意数量的参数。函数exact->inexact
用于把分数转换为浮点数。由括号、标记(token)以及分隔符组成的式子,称为S-表达式。
(- 10 3 5) ;Value: 2
(* 2 3) ;Value: 6
(/ 29 3) ;Value: 29/3
(exact->inexact(/ 29 3 7)) ;Value: 1.380952380952381
(* (+ 2 3) (- 5 3)) ;Value: 10其他算术
(quotient 7 3) ;Value: 2
(modulo 7 3) ;Value: 1
(sqrt 8) ;Value: 2.8284271247461903
(sin 1) ;Value: .8414709848078965
(expt 2 5) ;Value: 32生成表
Cons单元
Cons单元(Cons
cells)是一个存放了两个地址的内存空间,可用函数cons生成。
(cons 1 2) ;Value: (1 . 2)函数cons给两个地址分配了内存空间,存放指向1的地址的内存空间被称作car部分,存放指向2的地址的内存空间被称作cdr部分。

(cons 3 (cons 1 2)) ;Value: (3 1 . 2)其内存空间如下图:

其他例子如下:
(cons (cons 1 2) 3) ;Value: ((1 . 2) . 3)
(cons #\a (cons 3 "hello")) ;Value: (#\a 3 . "hello")表
表是Cons单元通过用cdr部分连接到下一个Cons单元的开头实现的。表中包含的’()被称作空表。就算数据仅由一个Cons单元组成,只要它的cdr单元是’(),那它就是一个表。

原子
不使用Cons单元的数据结构称为原子(atom)。数字、字符、字符串、向量和空表’()都是原子。’()既是原子,又是表。
引用
所有记号都会从最内层的括号依次向外层括号求值,最外层括号返回的值将作为S-表达式的值。引用(quote)可以用来阻止记号被求值,quote简写为',比如’(+ 2 3)代表列表(+ 2 3)本身,空表'()前面的'也是引用。
(+ 2 3) ;Value: 5
(quote(+ 2 3)) ;Value: (+ 2 3)
'(+ 2 3) ;Value: (+ 2 3)
'+ ;Value: +car和cdr函数
car和cdr函数会返回一个Cons单元的car部分和cdr部分。
(car '(1 2 3 4)) ;Value: 1
(cdr '(1 2 3 4)) ;Value: (2 3 4)list函数
用于构建表,可以有任意个参数。
(list) ;Value: ()
(list 1 2) ;Value: (1 2)
(list '(1 2) 3) ;Value: ((1 2) 3)函数
define
define用于声明变量,它接受两个参数。define会使用第一个参数作为全局参数,并将其与第二个参数绑定起来。
(define vhello "Hello world") ;绑定字符
(define fhello (lambda () "Hello world")) ;绑定过程vhello ;Value: "Hello world"
fhello ;Value: #[compound-procedure 17 fhello]
(fhello) ;Value: "Hello world"lambda
lambda用于定义过程,其第一个参数是过程所需的参数列表。
(define hello
(lambda (name)
(string-append "Hello " name "!")))
(define sum3
(lambda (a b c)
(+ a b c)))(hello "Lucy") ;Value: "Hello Lucy!"
(sum3 10 20 30) ;Value: 60或者使用如下简写:
(define (hello name)
(string-append "Hello " name "!"))
(define (sum3 a b c)
(+ a b c))分支
if
格式如下:
(if predicate then_value else_value)如果predicate部分为真,那么then_value部分被求值,否则else_value部分被求值,并且求得的值会返回给if语句的括号外。true是除false以外的任意值,true使用#t表示,false用#f表示。
可以省去else_value项,这样的话,当predicate为假时,返回值就没有被指定。如果希望当predicate为假时返回#f,那么就要明确地写出来。
(define (myabs x y)
(if (> x y)
(- x y)
(- y x)
)
)函数
null?用来判断表是否为空。(null? '()) ;Value: #t函数
not可用于对谓词取反。(not #t) ;Value: #f
cond
用于处理多个条件的情况,格式如下:
(cond
(predicate_1 clauses_1)
(predicate_2 clauses_2)
......
(predicate_n clauses_n)
(else clauses_else))在cond表达式中,predicates_i按照从上到下的顺序求值,而当predicates_i为真时,clause_i会被求值并返回。i之后的predicates和clauses不会被求值。如果所有的predicates_i都是假的话,则返回cluase_else。
(define (score n)
(cond
((>= n 80) 'A)
((<= 60 n 79) 'B)
((<= 40 n 59) 'C)
(else 'D)
)
)eq?、eqv?和equal?
eq?:比较两个对象的地址,如果相同的话就返回#t;eqv?:比较两个存储在内存中的对象的类型和值,如果类型和值都一致的话就返回#t;equal?:用于比较表或者字符串一类的序列。
用于比较数的函数
=、>、<、<=、>=都有任意个数的参数。
局部变量
let表达式是lambda表达式的一个语法糖。格式如下:
(let binds body)在binds中声明的变量可以在body里使用。
(let ((i 1) (j 2))
(+ i j))
;Value: 3递归
函数式程序中没有循环,循环是通过递归实现的。
尾递归是指递归调用发生在函数的最后一个语句中。这样函数调用就无需保存多个栈帧,当计算结束时直接将其返回。(Scheme规范要求尾递归调用转化为循环,因此尾递归调用就不存在函数调用开销)
;判断元素是否在列表中
(define (member atm lis)
(cond
((null? lis) #f)
((eq? atm (car lis)) #t)
(else (member atm (cdr lis)))
)
);求两个列表的交集
(define (guess lis1 lis2)
(cond
((null? lis1) '())
((member (car lis1) lis2)
(cons (car lis1) (guess (cdr lis1) lis2)))
(else (guess (cdr lis1) lis2))
)
)高阶函数
高阶函数(Higher Order Function)是一种以函数为参数的函数。
map
(map procedure list1 list2 ...)map将procedure应用于列表中的每个元素,返回一个包含结果的新列表。procedure是与某个过程或lambda表达式相绑定的符号,参数表的个数视procedure需要的参数而定。
(map + '(1 2 3) '(4 5 6)) ;Value: (5 7 9)
(map (lambda (x) (* x x)) '(1 2 3)) ;Value: (1 4 9)filter
filter
函数接受一个谓词函数和一个列表,返回满足谓词函数条件的元素的新列表。
(filter (lambda (x) (> x 2)) '(1 2 3 4 5))
;Value: (3 4 5)reduce
(reduce procedure init list)reduce是一个将列表元素通过某个操作归约为单一结果的函数。
(reduce + 0 '(1 2 3 4)) ;Value: 10
(reduce (lambda (x y) (* x y)) 1 '(1 2 3 4)) ;Value: 24sort
(sort '(3 5 1 4 -1) <) ;Value: (-1 1 3 4 5)