算数操作
基本算数操作
- 函数
exact->inexact
用于把分数转换为浮点数. - 函数
quotient
用于求商数(quotient)。 - 函数
remainder
和modulo
用于求余数(remainder)。 - 函数
sqrt
用于求参数的平方根(square root)。
1 | (exact->inexact (/ 29 3 7)) ;-> 1.380952380952381 |
练习
练习中发现, 如果创建 scm 文件并写入多个算式, 好像不能得出每个计算的值, 得到的只能是最后一个算式的结果.
表
空表
'()
即为空表
有两种非常类似的 value, 实现的方式却是不同的:
(3 2 . 1)
:(1 2 3)
:
其实主要的差异是最后一个 cons 的 cdr 指向地址的不同, 上面一个是指向 1 所在的地址, 最后一个是指向一个空表 '()
引用
'()
这里的空表就是使用了引用(quote)
list 函数
特别值得注意的是, list 函数返回的是其中的元素构成的表, 函数中的每个参数都是表的一个元素:
1 | (list '(1 2) '(3 4)) |
字符串操作
string-append
: 字符串拼接
函数定义
定义变量
1 | (define vhello "Hello world!") |
定义函数
定义函数有两种方式, 一个是 lambda 一个是简写, 暂时不清楚两者的实际区别.
1 | ;;; 定义不带参数的函数 |
if 判断
true and false
在 LISP 中 true 用 #t
表示, false 用 #f
表示.
关于空表的判断
由于 R4RS 和 R5RS 对空表 '()
是否为 false (#f
) 的标准不同, 所以判断表为空应使用 null?
:
1 | (null? '()) |
数字判断
positive?
: 判断正数zero?
: 是否为 0negative?
: 判断负数odd?
: 奇数even?
: 偶数
原生函数
integer->char
: 正数转化为 ascii 码
and 和 or
Scheme 中的 and 和 or 相对 C 类语言来说十分特殊:
and
具有任意个数的参数,并从左到右对它们求值。
如果某一参数为#f
,那么它就返回#f
,而不对剩余参数求值。
反过来说,如果所有的参数都不是#f
,那么就返回最后一个参数的值。
or
具有可变个数的参数,并从左到右对它们求值。
它返回第一个不是值#f
的参数,而余下的参数不会被求值。
如果所有的参数的值都是#f
的话,则返回最后一个参数的值。
关于 or
逻辑的最后依据可以看作 or 会从左往右求职到第一个非 #f
的值, 如果没有 #f
会一直计算到最后一个参数, 并返回其结果 #f
判断函数
eq?
, eqv?
and equal?
eq?
用于判断两个对象的地址, 适合用来比较两个变量.eqv?
比较两个存储在内存中的对象的类型和值, 如果类型和值都一致的话就返回#t
. 适合用来比较数值, 但是 int 和 float 比较的返回为#f
equal?
用于比较类似于表或者字符串一类的序列
单一变量的判断
pair?
: 如果对象为序对则返回#tlist?
: 如果对象是一个表则返回#t。要小心的是空表’()是一个表但是不是一个序对null?
: 如果对象是空表’()的话就返回#tsymbol?
: 如果对象是一个符号则返回#tchar?
: 如果对象是一个字符则返回#tstring?
: 如果对象是一个字符串则返回#tnumber?
: 如果对象是一个数字则返回#tcomplex?
: 如果对象是一个复数则返回#treal?
: 如果对象是一个实数则返回#trational?
: 如果对象是一个有理数则返回#tinteger?
: 如果对象是一个整数则返回#texact?
: 如果对象不是一个浮点数的话则返回#tinexact?
: 如果对象是一个浮点数的话则返回#t
字符比较
char=?
, char<?
, char>?
, char<=?
, char>=?
比较字符串
string=?
, string-cli=?
关于循环和 lambda
一个分别接受一个表
ls
和一个对象x
的函数,该函数返回从ls
中删除x
后得到的表。
后面给出了习题答案, 但是处理 “过于优雅”, 对初学者不是很友好, 在这个回答中可以找到更易懂的解题方法. 下面把两种做一个对照:
浅谈 lambda
原答案中最让人纠结的地方就是 if… lambda… 的组合使用, 由于之前本课程中没有过对于 lambda 的介绍, 只有 define 函数的时候提到过, 所以难免不清楚怎么使用. 其实可以在终端中动手试试, 你会发现 lambda 的用法如下:
1 | ((lambda (x) (* 2 x)) 1) |
以上就是定义 lambda 匿名函数的方法, 在最外层的括号中有两个组成部分, 一个是定义 lambda 匿名函数的括号, 还有一组参数. 再对应到原书中的答案, 就可以发现原答案中是根据判断定义匿名函数, 并作用在 remove x (cdr ls)
上. 这样考虑就简单了许多.
named let and letrec
named let 用于迭代, 而 letrec 用于递归.
在 named let 中使用 let
初始化变量, 并在每次执行到命名的函数时循环. letrec
允许递归调用本身.
从实践来看, named let 解决了 while 类循环在 scheme 中的实现, 但是 letrec
感觉只是为函数定义限制了作用域.
映射
1 | (map procedure list1 list2 ...) |
procedure
是个与某个过程或 lambda 表达式相绑定的符号。作为参数的表的个数视 procedure
需要的参数而定。
Reduce
关于 reduce 例子, 书里说的十分含糊, 经过资料查找, 可以发现示例来自GNU MIT-Scheme的官方文档, 这里可以了解到:
The argument initial is used only if list is empty;
Apply 函数
这个函数类似于普通语言中对函数的使用方法, 由 apply
, 固定数量的参数, 和不固定参数构成的表组成, 类似于 Python 中的 func(*args, **kwargs)
词法闭包
作用域与源代码书写方式一致的作用域称为“词法闭包(Lexical closure)”或“静态作用域(Static scope)”。
关联表
函数assq
,assv
,和assoc
从关联表中搜寻一个项。这些函数从开始一步步搜索关联表。如果它们找到序对的car
等于给定的key
,就返回该序对。如果找不到函数返回#f
。这些函数分别使用eq?
,eqv?
,和equal?
比较键,这意味着assq
最快,assoc
最慢。