重用复杂数据结构
有时,我们会希望函数是和其他语言中的函数一样的运行,即在不修改传入参数的情况下返回个新值,而有时,我们又想让函数重用或在数据的原有基础上进行修改 —— 想想 [append](http://www.lispworks.com/documentation/HyperSpec/Body/f_append.htm)
和 [nconc](http://www.lispworks.com/documentation/HyperSpec/Body/f_nconc.htm)
之间的区别吧。或者是 C 语言函数中的传值与传址。
要想实现上面所说的功能,可以使用可选(关键词)参数。如:假设需要实现一个名为 complex-matrix-stuff
的函数,该函数有两个参数 m1
和 m2
,然后函数会计算出矩阵 m1
和 m2
运算结果并返回所得矩阵的大小,也就是说,在调用函数时将会由 (make-appropriate-result-matrix-for m1 m2)
创建一个空矩阵用来存储结果。
标准的教科书式的实现方法看起来和下面的差不多:
(defun complex-matrix-stuff (m1 m2)
(let ((result (make-appropriate-result-matrix-for m1 m2)))
;; ... compute storing the results in RESULT
result))
然后就可以这样调用:
(setq some-matrix (complex-matrix-stuff A B))
但为什么不写成下面这种形式呢?
(defun complex-matrix-stuff (m1 m2
&optional
(result
(make-appropriate-result-matrix-for m1 m2)))
;; ... compute storing the results in RESULT
result)
现在就有两种方法了,而且依然可以像之前一样使用:
(setq some-matrix (complex-matrix-stuff A B))
但也可以直接将结果赋给已经存在的变量:
(complex-matrix-stuff A B some-appropriate-matrix-I-built-before)
或者这样:
(setq some-other-matrix
(complex-matrix-stuff A B some-appropriate-matrix-I-built-before))
得到的结果是一样的:
* (eq some-other-matrix some-appropriate-matrix-I-built-before)
T
合并新的序列时用 ADJUST-ARRAY
而不是 SUBSEQ
CL 中大部分对序列操作的函数都可以使用 start
和 end
关键字,因此,可以不创建子字符串就进行操作。即,不要像下面这样:
(count #\a (subseq long-string from to))
而是要这样使用:
(count #\a long-string :start from :end to)
上面这种做法得到的结果与之前的是一样的,但不会创建不必要的中间子序列。
然而,有时创建新的数据无法避免。这时就可以将字符串当作是哈希表的关键词。当查找的键是另一个字符串的子串时,可以像下面这样:
(gethash (subseq original-string from to)
hash-table)
但没必要这么做。可以创建一个可变字符串,然后通过 [adjust-array](http://www.lispworks.com/documentation/HyperSpec/Body/f_adjust.htm)
对该字符串进行重用:
(let ((substring (make-array 0
:element-type 'character
:displaced-to ""
:displaced-index-offset 0)))
;; more code
(gethash
(adjust-array substring (- to from)
:displaced-to original-string
:displaced-index-offset from)
hash-table)
;; even more code
)