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: 24
sort
(sort '(3 5 1 4 -1) <) ;Value: (-1 1 3 4 5)