Lisp学习5

来源:互联网 发布:1元域名注册 编辑:程序博客网 时间:2024/06/14 08:19
--------------------2015-3-16-------------------
            
        如果你特定想要重设由 DEFVAR 定义的变量,要么使用 SETF直接设置它,要么使用 MAKUNBOUND 先将其变成未绑定的,再重新求值 DEFVAR 形式。

        在用 DEFVAR 和 DEFPARAMETER 定义一个变量之后,就可以从任何一个地方引用它。
        如:(defun increment-widget-count () (incf *count*))

        如果想要临时重定义全局变量(如:*standard-output*),只需要将其重新绑定即可,比如可以用 LET :
        (let ((*standard-output* *some-other-stream*))
            (stuff))
        * 在任何由于调用 stuff 而运行的代码中,对 *standard-output* 的引用将使用由 LET 所创建的绑定,并且当 stuff 返回并且程序控制离开 LET 时,这个对 *standard-output* 的新绑定就随之消失,接下来对 *standard-output* 的引用将回到 LET 之前的绑定。(在任何给定时刻,最近建立的绑定会覆盖所有其他的绑定)

        一个简单的例子:
        (defvar *x* 10)
        (defun foo () (format t "x: ~d~%" *x*))

        CL-USER> (foo)
        x: 10
        NIL

        CL-USER> (let ((*x* 20)) (foo))
        x: 20
        NIL

        CL-USER> (foo)
        x: 10
        NIL

        (defun bar ()
            (foo)
            (let ((*x* 20)) (foo))
            (foo))

        CL-USER> (bar)
        x: 10
        x: 20
        x: 10
        NIL

    常量:
        所有的常量都是全局的,并且使用 DEFCONSTANT 定义:
        (defconstant name initial-value-form [documentation-string])
        与 DEFVAR 和 DEFPARAMETER 相似, DEFCONSTANT 在所使用的名字上产生了全局效果——从此该名字仅被用于指向常量,它不能被用作函数形参或是任何其他的绑定形式进行重绑定。
        一般用 +constant-variable-name+ 的形式来表示常量

    赋值:
        为绑定赋予新值需要使用 SETF 宏—— Common Lisp 的通用赋值操作符,其基本形式为:
        (setf place value)
        如:
        (setf x 10)     ; 将值 10 赋给变量 x

        为一个绑定赋予新值对该变量的任何其他绑定没有效果,并且它对赋值之前绑定上所保存的值也没有任何效果。

        SETF 也可用于依次对多个位置赋值。
        如:
        (setf x 1)
        (setf y 2)
        可以写成:
        (setf x 1 y 2)

        SETF 返回最近被赋予的值,因此也可以像下面的表达式那样嵌套调用 SETF ,将 x 和 y 赋予同一个随机值:
        (setf x (setf y (random 10)))

    广义赋值:
        Simple variable:            (setf x 10)
        Array:                                (setf (aref a 0) 10)
        Hash table:                        (setf (gethash 'key hash) 10)
        Slot named 'field':        (setf (field o) 10)

        * AREF 是数组访问函数,GETHASH 做哈希表查找。

    其他修改位置的方式:
        (incf x)                ≡            (setf x (+ x 1))
        (decf x)                ≡            (setf x (- x 1))
        (incf x 10)            ≡            (setf x (+ x 10))

        * 类似 INCF 和 DECF 这种宏称为 修改宏(modify macro)

        ROTATEF:
            在位置之间轮换他们的值:
            (rotatef a b)        ; 将交换两个变量的值并返回NIL
            * 等价于(let ((tmp a)) (setf a b b tmp) nil)

        SHIFTF:
            它将值向左侧移动而不轮换它们:
            (shiftf a b 10) ; 将 b 的值赋给 a ,将 10 赋值给 b
            * 等价于(let ((tmp a)) (setf a b b 10) tmp)

**宏:标准控制构造

    WHEN 和 UNLESS:
        * 特殊操作符 PROGN 可以按顺序执行任意数量的形式并返回最后一个形式的值。

        (if (spam-p current-message)
            (progn
                (file-in-spam-folder current-message)
                (update-spam-database current-message)))
        等价于:
        (when (spam-p current-message)
            (file-in-spam-folder current-message)
            (update-spam-database current-message))
        * 如果 WHEN 没有被内置到标准库中,可以像下面这样用一个宏来自己定义 WHEN:
        (defmacro when (condition &rest body)
            `(if ,condition (progn ,@body)))

        与 WHEN 宏同一个系列的另一个宏是 UNLESS, 它取相反的条件,只有当条件为假时才求值其形式体,相当于:
        (defmacro unless (condition &rest body)
            `(if (not ,condition) (progn ,@body)))

    COND:
        用于表达多重分支条件的宏 COND,它的基本结构:
        (cond
            (test-1 form*)
                .
                .
                .
            (test-N form*))
        主体中的每个元素都代表一个条件分支,并由一个列表所构成,列表中含有一个条件形式,以及零或多个当该分支被选择时将被求值的形式。
        习惯上,那个用来表示 if/else-if 链中最后一个 else 子句的分支将被写成带有条件T。虽然任何非 NIL 的值都可以使用,但在阅读代码时, T 标记确实有用。
        如:
        (cond (a (do-x))
                    (b (do-y))

                    (t (do-z)))

    AND、OR 和 NOT(布尔逻辑操作符):
        NOT:
            接受单一参数并对其真值取反,当参数为 NIL 时返回 T,否则返回 NIL。
        AND 和 OR 则是宏:
            实现了对任意数量子表达式的逻辑合取和析取操作,并被定义成宏以便支持“短路”特性。
        如:
        (not nil)                                 →        T
        (not (= 1 1))                            →     NIL
        (and (= 1 2) (= 3 3))            →        NIL
        (or (= 1 2) (= 3 3))            →     T

    循环:
        DOLIST:
            在一个列表的元素上循环操作,使用一个依次持有列表中所有后继元素的变量来执行循环体。
            形如:
            (dolist (var list-form) body-form*)
            例如:
            CL-USER> (dolist (x '(1 2 3)) (print x))
            1
            2
            3
            NIL
            在这种方式下,DOLIST 这种形式本身求值为 NIL
            如果想在列表结束之前中断一个 DOLIST 循环,则可以使用 RETURN
            CL-USER> (dolist (x '(1 2 3)) (print x) (if (evenp x)(return)))
            1
            2
            NIL            
        DOTIMES:
            用于循环计数的高级循环构造,形如:
            (dotimes (var count-form) body-form*)
            其中的 count-form 必须要能求值为一个整数。通过每次循环,var 所持有的整数依次为从 0 到比那个数小 1 的每一个后继整数。
            如:
            CL-USER> (dotimes (i 4) (print i))
            0
            1
            2
            3
            NIL

        

小应用(输出9*9乘法表):        (dotimes (i 9)            (dotimes (j 9)                (format t "~3d * ~d = ~3d " (+ 1 i) (+ 1 j) (* (+ 1 i) (+ 1 j))))            (format t "~%"))


0 0
原创粉丝点击