[dynamic FL]part 5:基本概念
Racket: 动态语言,而非静态
Racket和Scheme同源,来自LISP。可以讲任何类型的数据放在任何相仿的地方
Racket由一组definition组成,括号用来求值e并调用函数。
括号内可以有atom: 包含特定值,变量和关键字(define lambda if)。
所有的东西都是前缀形式,如2+3=>(+ 2 3)
(t1 t2 …):如果t1是关键字:有特殊用处的表达式
如果t1不是关键字:函数调用
top-level definitions:ML中的所有binding类似let*,Racket中的definition类似letter。
注意:1.不能同名
2.如果前面的binding使用后面的,放在函数里
3.没有top-level shadowing(不能同名),但是一个模块能shadow另一个文件中的binding
mutate: (set! x e)
如果调用函数,函数会找某个值在上下文中的最终值,可能是修改后的。如果要使用之前的值,在函数中拷贝一份
top-level binding不会在其他地方改变,除非定义它的模块显式的set!了
只改变x指向的对象,不能改变对象的内部。如果对象是list,要改变内部需要用mcons等等改变
lazy evaluation: call-by-need, promise. 使用mutation + list记住最初使用thunk的值,以后就不用再次调用thunk了。
thunk:使用无参数的匿名函数来delay evaluation:Racket中,调用(e1 x y)时,总是会先evaluate x,y然后再进入函数体。然而如果是lambda(…)…,则会在调用的时候才evaluate。这样(e1 (lambda() x) (lambda() y))就不会evaluate x, y了,只有函数体显示调用函数的时候才会evaluate。使用lambda()
x替代x
my-delay是一个函数/数据结构,传入一个thunk f, 如果没有调用过f,则返回(#f, f),如果要调用,则将my-delay改为(#t, f的调用结果)
例子:仅仅使用thunk
call:![[dynamic FL]part 5:基本概念 [dynamic FL]part 5:基本概念](/default/index/img?u=aHR0cHM6Ly9waWFuc2hlbi5jb20vaW1hZ2VzLzUyNS83OWI2NTc4MWQ3MDRhZjBiOGJlMjlmYjA2OTFhMGIxZC5wbmc=)
使用force-delay+thunk
call:![[dynamic FL]part 5:基本概念 [dynamic FL]part 5:基本概念](/default/index/img?u=aHR0cHM6Ly9waWFuc2hlbi5jb20vaW1hZ2VzLzg0Mi81YWRlZDQ5ZGYyNDE0MjFmNWYxNWYzMjhkY2FkY2JjYS5wbmc=)
另一种:
call:![[dynamic FL]part 5:基本概念 [dynamic FL]part 5:基本概念](/default/index/img?u=aHR0cHM6Ly9waWFuc2hlbi5jb20vaW1hZ2VzLzEyMC9jZjUwNjQxNGNhM2UwZjgzOTJiMzQ3YTI4MGQwMmMwMC5wbmc=)
stream:是一个无穷序列
用thunk表示stream,当调用的时候:生成序列中的一个值,并且返回产生下一个值的thunk
memorization: 使用memo table
macro:(define-syntax x (syntax-rules (rest of x’s key words)
[(how to write macro) (macro-expansion)]))
允许程序员定制自己的语法糖
macro use:使用宏的地方
macro define:定义宏
macro expansion:使用宏定义中的内容替换使用宏的地方。发生在一切之前,在type-checking, compiling...之前
tokenization:将程序分解成token,因此a->b并不意味着at->bt
parenthesization:Racket中的宏展开保持了原有的函数结构,宏展开的结果和宏使用都放在一个括号内
scope:宏展开不适用于变量定义,自带的car等等会shadow同名的宏定义
只有宏能通过添加thrunk实现evaluation延迟,函数必须传入thunk,因为函数一定会evaluate参数
local variable: 宏中+e e不等于* 2 e,需要使用let将e的值存起来为x,再对x做处理。
hygiene: 在使用宏的时候,let中虽然传入的可能是一个名为x的自由变量,但是在Racket中会用不同名字表示(rewritten),不会和宏定义中的let [x e]中的x混淆;
宏定义中使用的自由变量来自定义宏的时候的上下文,而不是使用宏的上下文。因此不会被使用宏时的自由变量影响
definition: (define x e)
anonymous function: (lambda (x) e)
(define f (lambda(x) e))
和ML不同,lambda函数可以使用递归,因为它的定义在函数体的上下文中
currying:
(define f ((lambda(x) (lambda(y) e))) 或者 (define ((f x) y) e) =>((f x) y)
#t, #f:True, False,除了#f其他都是True
list: 可含有不同类型的元素
null: 空list
cons: 创建list(cons 2(cons 3 null))。实际上cons只是创建一个pair,前面的部分能用car访问,后面的部分能用car访问。如果不是接的null,则是pair,也叫作improper list。improper list可以用来创建each-of类型,最好用proper-list创建长度会变化的collection
car: 获取第一个元素
cdr: 获取除第一个外的其他元素
null?: 检查是否是空list
list: 创建list (list 2 3)
cond: (cond [e1 e2]
[e1 e2]...)
类似switch...case, e1是条件,e2是条件为真时执行的语句
let: (let ([x1 e1]
[x2 e2]...)
e)
使用let之前的环境evaluate ei,e2中不能用x1
let*:使用let之前的环境以及let*中前面的binding evaluate ei,e2可以用x1,但是e1不能用x2,因此不能递归
和ML的let相同。嵌套的let就是let*
letrec:可以使用同一组let中之前和之后的上下文。通常用于定义相互引用的函数
注意,虽然可以使用,但是每个binding还是依次evaluate的。相互引用的函数中,可以调用另一个函数并且不会出问题,因为函数没有调用,就不会使用变量,所以在第一个函数中调用没有evaluate的第二个函数没有问题。定义值则有问题。
struct: (struct foo (bar bah) #:transparent)
foo:类似ML的constructor,在环境中添加 创建foo的函数,参数有两个分别给bar和bah
foo?: 测试是否是foo的函数
foo-bar:提取foo的bar的内容的函数
attribute: #:transparent:允许fields和accessor function在模块外可见
#:mutable:允许通过set-foo-bar!来修改fields
创建一种新的类型的数据结构,能够尽早发现错误,能够隐藏部分接口,使得模块外不可见