【Scheme】Scheme 编程学习 (五) —— 引述


原视频地址:https://www.bilibili.com/video/BV1Kt411R7Wf?p=5

Quoting in Scheme, powerful way to building code, using code。
在 Scheme 中的引述,是一种强大的方式,用于构建代码,使用代码。

  • Data made from code 从代码中创建数据
  • Treating data as code 如何以代码的方式处理数据
  • Example: generic type safety 举例:以一种通用的方式来使用类型安全
    (type-safe code in a generic way)

1. Data made from code

(list "a" "b" "c")
;("a" "b" "c")

获得三个字符串组成的表

如果不创建三个字符串的表,而创建三个符号(symbol)的表呢?

(list a b c)
; reference to undefined identifier: a

失败,由于 a,b, c 为未定义的标识符,需要定义 a,b, c 。

那么如何使用储符号,而不是它们所代表的值 (not what they refer to),需要使用引述(quoting)

在 scheme 中引述,只需要使用单引号,如下:

(list 'a 'b 'c)
;(a, b, c)

我们如何嵌套这些东西 (How we can do to nest this stuff)?

(list 'a (list 'b1 'b2) 'c)
; (a (b1 b2) c)

在整个列表开始处加上单引号,也能将整个列表当作符号来处理。此种表达方式与前者等价。

'(a (b1 b2) c)
;(a (b1 b2) c)

使用引述就是告诉编译器,对这个符号什么都不要做,不去查找(look up), 只存储这个符号。

'a
;a
a
; reference to undefined indentifier: a

empty list 空表

> ()
; missing procedure expression

执行空表失败,因为需要表中至少有一项内容。

> (list)
; ()
> '()
;()

donnot execute this thing just hang out. 不执行这条语句,只是挂出来。

> null
; ()
> 'null
; null; we get back the symbol null

make data that looks like code
how we can query data

(define z 1)
(symbol? 'z)
; #t
(symbol? z)
; #f ; in this case z will be evaluated, be replaced with the value 1, and 1 is not a symbol so false
(number? 'z)
; #f
(number? z)
; #t

2. Treating data as code

将数据视为代码

(define forty-two '(* 6 9))
> forty-two
; (* 6 9) ;forty-two is a list
> (eval forty-two)
; evaluate take a list and execute for it
; 54

3. Example: generic type safety

通用类型的安全性,scheme 为弱类型语言,所以需要类型检查。示例:
检查整数

(define (safe-sum x y)
	(if (and (integer? x) (integer? y))
		(+ x y)
		"INCORRECT TYPES"))

定义一个过程,如果 x 和 y 都为整数,则输出执行 (+ x y) 的结果,否则输出 “INCORRECT TYPES”,示例:

> (safe-sum 1 2)
;3
> (safe-sum 1.1 2.4)
;"INCORRECT TYPES"

检查浮点数

(define (nonint? x)
	(and (real? x) (not (integer? x))))
; 返回 x 是否为 real 和 非integer 的与结果

(define (safe-sum x y)
	(if (and (nonint? x) (nonint? y))
		(+ x y)
		"INCORRECT TYPES"))
; 如果 x y 都符合 nonint? 则返回 (+ x y) 的执行结果,否则返回"INCORRECT TYPES"
 		
> (safe-sum 1 2)
; "INCORRECT TYPES"
> (safe-sum 1.1 2.4)
;3.5
(define (replace lst find repl)
	(if (pair? lst)
		(cons
			(replace (car lst) find repl)
			(replace (cdr lst) find repl))
		(if (equal? find lst)
			repl
			lst)))			

此函数定义,乍看起来很难,好像又是关键字又是什么的,
其实是定义了一个函数,函数名为 replace 有三个参数,lst, find, repl,
lst - 表,
find - 期望查找的内容,
repl - 期望替换为的内容

如果表为点对则创建一个点对 cons,replace car, replace cdr 递归调用。

主干内容为,如果在 lst 中找到了 find,则返回为 repl,否则返回整个表。

使用举例

> (replace '(1 2 3) '2 "XXX")
; (1 "XXX" 3)

将表 '(1 2 3) 中的 '2 符号替换为 “XXX”

> (replace '(a (b1 b2) c) 'b1 'BOO)
; (a (BOO b2) c)

将表 '(a (b1 b2) c) 中的 'b1 符号替换为 'BOO 符号

前面定义了两种类型安全的和计算 safe-sum
这里我们可以定义一个通用类型的函数

(define generic-safe-sum
	; an quoted expression
	'(define (safe-sum x y)
		(if (and (TEST x) (TEST y))
			(+ x y)
			"INCORRECT TYPES")))

与前两者基本类型,不同的地方为,这里我们定义 TEST 为一个通用类型的检查。注意 函数定义之前有个(quoting)引述 ‘ ,所以在定义时,这部分代码都被视作符号不进行执行 (evaluate)。

如果我们想知道 generic-safe-sum 的内容,则会完整的输出这个引述的表达式。

> generic-safe-sum
; (define (safe-sum x y)
;		(if (and (TEST x) (TEST y))
;			(+ x y)
;			"INCORRECT TYPES"))

然后我们定义一个整数类型检查并查看

> (define safe-sum-q
	(replace generic-safe-sum 'TEST 'integer?)) 
	; 将通用类型的检查替换为 整数检查
> safe-sum-q
; (define (safe-sum x y)
;		(if (and (integer? x) (integer? y))
;			(+ x y)
;			"INCORRECT TYPES"))

与原 generic-safe-sum 几乎一致,除了 TEST 被替换为了 integer?,仍然是一个完整的引述表达式。
safe-sum-q 为一个表,不为函数,只有 evaluate (eval) 时才会执行。

> (eval safe-sum-q)
; 执行safe-sum-q,这里我们定义了一个函数 safe-sum 
; (define (safe-sum x y)
;		(if (and (integer? x) (integer? y))
;			(+ x y)
;			"INCORRECT TYPES"))
> (safe-sum 1 2)
; 3
> (safe-sum 1.1 2.4)
; "INCORRECT TYPES"

本节通过示例,我们更清晰的知道了引述的用途,以及使用的方法。

10-16 06:48