ANSI Common Lisp 学习笔记 第六章

scinart posted @ 2013年6月27日 00:01 in Lisp , 2016 阅读

早就看了,只是才做习题。

* 第六章:函数
** 6.1 全局函数 (Global Functions)
*** fboundp symbol-name
谓词 fboundp 告诉我们,是否有个函数的名字与给定的符号绑定。如果一个符号是函数的名字,则 symbol-name 会返回它:
*** symbol-function
可通过 symbol-function 给函数配置某个名字:
(setf (symbol-function 'add2)
  #'(lambda (x) (+ x 2)))
新的全局函数可以这样定义,用起来和 defun 所定义的函数一样 : 

(add2 1)
*** documentation string
如果让字串成为 defun 定义的函数主体的第一个表达式,
(defun foo (x)
  "Implements an enhanced paradigm of diversity"
  x)
那麽这个字串会变成函数的文档字串(documentation string)。要取得函数的文档字串,可以通过调用 documentation 来取得:
(documentation 'foo 'function)
"Implements an enhanced paradigm of diversity"
** 6.2 局部函数 (Local Functions)
局部函数可以使用 labels 来定义,它是一种像是给函数使用的 let 。它的第一个实参是一个新局部函数的定义列表,而不是一个变量规格说明的列表。列表中的元素为如下形式:
(name parameters . body)
而 labels 表达式剩馀的部份,调用 name 就等於调用 (lambda parameters . body) 。
(labels ((add10 (x) (+ x 10))
         (consa  (x) (cons 'a x)))
  (consa (add10 3)))
;; (A . 13)
** 6.3 参数列表 (Parameter Lists)
*** &rest
&rest 符号,那麽当这个函数被调用时,这个变量会被设成一个带有剩馀参数的列表
*** &optional
(defun philosoph (thing &optional (property 'fun))
  (list thing 'is property))
(philosoph 'death)
(DEATH IS FUN)
选择性参数的缺省值可以不是常量。可以是任何的 Lisp 表达式。若这个表达式不是常量,它会在每次需要用到缺省值时被重新求值。
*** &key
一个关键字参数(keyword parameter)是一种更灵活的选择性参数。如果你把符号 &key 放在一个形参列表,那在 &key 之後的形参都是选择性的。此外,当函数被调用时,这些参数会被识别出来,参数的位置在哪不重要,而是用符号标签(译注: : )识别出来:
(defun keylist (a &key x y z)
  (list a x y z))
(keylist 1 :y 2)
;; (1 NIL 2 NIL)
(keylist 1 :y 3 :x 2)
;; (1 2 3 NIL)
和普通的选择性参数一样,关键字参数缺省值为 nil ,但可以在形参列表中明确地指定缺省值。
** 6.4 示例:实用函数 (Example: Utilities)
(defun single? (lst)
  (and (consp lst) (null (cdr lst))))

(defun append1 (lst obj)
  (append lst (list obj)))

(defun map-int (fn n)
  (let ((acc nil))
    (dotimes (i n)
      (push (funcall fn i) acc))
    (nreverse acc)))

(defun filter (fn lst)
  (let ((acc nil))
    (dolist (x lst)
      (let ((val (funcall fn x)))
        (if val (push val acc))))
    (nreverse acc)))

(defun most (fn lst)
  (if (null lst)
      (values nil nil)
      (let* ((wins (car lst))
             (max (funcall fn wins)))
        (dolist (obj (cdr lst))
          (let ((score (funcall fn obj)))
            (when (> score max)
              (setf wins obj
                    max  score))))
        (values wins max))))
** 6.5 闭包 (Closures)
** 6.6 示例:函数构造器 (Example: Function Builders)
** 6.7 动态作用域 (Dynamic Sc​​ope)
** 6.8 编译 (Compilation)
当一个函数包含在另一个函数内时,包含它的函数会被编译,而且内部的函数也会被编译。所以 make-adder (108 页)被编译时,它会返回编译的函数:
(compile 'make-adder)
MAKE-ADDER
(compiled-function-p (make-adder 2))
T
** 6.9 使用递归 (Using Recursion)
** Chapter 6 总结 (Summary)
***    命名函数是一个存在符号的 symbol-function 部分的函数。 defun 宏隐藏了这样的细节。它也允许你定义文档字串(documentation string),并指定 setf 要怎麽处理函数调用。
***    定义局部函数是有可能的,与定义局部变量有相似的精神。
***    函数可以有选择性参数(optional)、剩馀(rest)以及关键字(keyword)参数。
***    实用函数是 Lisp 的扩展。他们是由下而上编程的小规模示例。
***    只要有某物引用到词法变量时,它们会一直存在。闭包是引用到自由变量的函数。你可以写出返回闭包的函数。
***    Dylan 提供了构造函数的函数。很简单就可以使用闭包,然后在 Common Lisp 中实现它们。
***    特别变量(special variable)有动态作用域 (dynamic scope)。
***    Lisp 函数可以单独编译,或(更常见)编译整个文件。
***    一个递归演算法通过将问题细分成更小丶更小的子问题来解决问题。
** Chapter 6 练习 (Exercises)
***    定义一个 tokens 版本 (67 页),接受 :test 与 :start 参数,缺省分别是 #'constituent 与 0 。(译注: 67 页在 4.5 小节)
(defun constituent (c)
  (and (graphic-char-p c)
       (not (char= c #\ ))))
(defun tokens (str &key (test #'constituent) (start 0))
  (let ((p1 (position-if test str :start start)))
    (if p1
    ;;找到第一个token
        (let ((p2 (position-if #'(lambda (c)
                   (not (funcall test c)))
                   str :start p1)))
      ;;P2是下一个不通过test的字符
          (cons (subseq str p1 p2)
                (if p2
                    (tokens str :test test :start p2)
                    nil)))
        nil)))
;; (tokens "1231 23  t21" :test #'(lambda (c) (char/= c #\2)))
***    定义一个 bin-search (60 页)的版本,接受 :key , :test , start 与 end 参数,有着一般的意义与缺省值。(译注: 60 页在 4.1 小节)
不知道为什么这个CL-USER::KEY 为什么老是找不到定义。
;; (defun bin-search (obj vec < > &key (key #'identity) (test #'eql) (start 0) (end 0 end-provided-p))
;;   ;; [start end)
;;   (let ((real-end (or (and end-provided-p end) (length vec))))
;;     (if (> (- real-end start) 0)
;;     (let ((mid (floor (/ (+ start real-end) 2))))
;;       (if (< obj (aref vec mid))
;;           (bin-search obj vec < > :key key :test test :start start :end mid)
;;           (if (> obj (aref vec mid))
;;           (bin-search obj vec < > :key key :test test :start (1+ mid) :end real-end)
;;           (funcall test obj (key (aref vec mid)))))))))

;; (bin-search 5 #(1 3 5 9) #'< #'>)
***    定义一个函数,接受任何数目的参数,并返回传入的参数。
(defun return-this (&rest p)
  (values p))
***    修改 most 函数 (105 页),使其返回 2 个数值,一个列表中最高分的两个元素。(译注: 105 页在 6.4 小节)
(defun most-two (lst < score)
  (labels ((f (lst a b < score)
         (if (null lst)
         (values a b)
         (cond ((< (car lst) b)
            (f (cdr lst) a b < score))
               ((< a (car lst))
            (f (cdr lst) (car lst) a < score))
               (t
            (f (cdr lst) a (car lst) < score))))))
    (let ((a (first lst))
      (b (first lst)))
      (f lst a b < score))))
***    用 filter (105 页) 来定义 remove-if (没有关键字)。(译注: 105 页在 6.4 小节)
***    定义一个函数,接受一个参数丶一个数字,并返回目前传入参数中最大的那个。
应该是
(let (...)
  (defun ...))
好久没看,转scheme思维了。
(setf (symbol-function 'challenge-me)
      (let ((temp))
    #'(lambda (x)
        (if temp
        (cond ((> x temp)
               (setq temp x)
               x)
              (t
               temp))
        (progn (setq temp x) nil)))))
***    定义一个函数,接受一个参数丶一个数字,若传入参数比上个参数大时,返回真。函数第一次调用时应返回 nil 。
(setf (symbol-function 'improve?)
      (let ((last-time nil))
    (lambda (x)
      (if last-time
          (cond ((> x last-time)
             (setq last-time x)
             x)
            (t
             nil))
          (progn (setq last-time x) nil)))))
***    假设 expensive 是一个接受一个参数的函数,一个介於 0 至 100 的整数(包含 100),返回一个耗时的计算结果。定义一个函数 frugal 来返回同样的答案,但仅在没见过传入参数时调用 expensive 。
(setf (symbol-function 'frugal)
      (let ((vec (make-array 101 :initial-element nil)))
    (lambda (x)
      (declare (fixnum x))
      (cond ((null (aref vec x))
         (setf (aref vec x) (cons t (expensive x)))
         (cdr (aref vec x)))
        (t
         (cdr (aref vec x)))))))

(defmacro defalias (nickname realname)
  `(setf (symbol-function ',nickname) #',realname))

***    定义一个像是 apply 的函数,但在任何数字印出前,缺省用 8 进制印出。

一些*print-base*的事。。。


登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter