关于onlisp中的广义变量

时间:2014-01-28 13:39:16

标签: lisp common-lisp

我不确定这里发生了什么,文中的一个宏示例。 基本上,不熟悉如何使用 get-setf-method ,一个内置的宏(可能是函数?)。 具体来说, get-setf-method 的某些返回值为零的情况怎么样? 例如     (get-setf-method'x)

NIL ;
NIL ;
(#:NEW-3069) ;
(SETQ X #:NEW-3069) ;
X

为什么这个示例代码首先将第五个返回值设置为第二个返回值,以进行初始化? 最后,它如何处理在表达式中设置变量的顺序,例如(aref ar(incf i)

(get-setf-method '(aref ar (incf i)))

(#:G3070 #:G3071) ;
(AR (INCF I)) ;
(#:G3072) ;
(SYSTEM::STORE #:G3070 #:G3071 #:G3072) ;
(AREF #:G3070 #:G3071)

以下是宏的定义:

(defmacro sortf (op &rest places)
  (let* ((meths (mapcar #'(lambda (p)
                             (multiple-value-list
                              (get-setf-method p)))
                        places))
         (temps (apply #'append (mapcar #'third meths))))
    `(let* ,(mapcar #'list
                     (mapcan #'(lambda (m)
                                  (append (first m)
                                          (third m)))
                             meths)
                     (mapcan #'(lambda (m)
                                  (append (second m)
                                          (list (fifth m))))
                             meths))
        ,@(mapcon #'(lambda (rest)
                       (mapcar
                        #'(lambda (arg)
                             `(unless (,op ,(car rest) ,arg)
                                 (rotatef ,(car rest) ,arg)))
                        (cdr rest)))
                  temps)
        ,@(mapcar #'fourth meths))))

1 个答案:

答案 0 :(得分:5)

这实际上是一些旧的代码。实际上get-setf-methodget-setf-expansion替换为Issue SETF-METHOD-VS-SETF-METHOD Writeup。所以你最近感兴趣的是get-setf-expansion。它返回的值是您在某个位置安全存储值所需的代码段。这非常重要,因为错误地编写modyfing宏非常容易。

至于为什么某些值可以是nilget-setf-expansion文档中的一个示例实际上显示了某些值如何nil

(get-setf-expansion 'x)
;=>  NIL, NIL, (#:G0001), (SETQ X #:G0001), X 

但这些价值观是什么?为此,我们需要查看文档的语法:

  

语法:

     

get-setf-expansion 放置&optional环境

     

vars,vals,store-vars,writer-form,reader-form

     

参数和值:

     

放置一个地方

     

环境 - 环境对象。

     

vars,vals,store-vars,writer-form,reader-form-a setf expansion。

5.1.1.2 Setf Expansions中描述了这五个返回值:

  

临时变量列表一个符号列表,用于命名要按顺序绑定的临时变量(就像let *一样)到结果值   从价值形式。

     

价值表单列表表单列表(通常是地点的子表单),在评估时会生成表格的值   应绑定相应的临时变量。

     

商店变量列表一个符号列表,用于命名临时存储变量,这些变量用于保存将分配给的新值   这个地方。

     

存储表单一个表单,该表单可以引用临时变量和存储变量,并且可以更改地点的值和   保证以值的形式返回商店变量的值,   这是setf返回的正确值。

     

访问表单表单,该表单可以引用临时变量,并返回该地点的值。

那么示例中的那些值是什么意思?

(get-setf-expansion 'x)
;⇒  NIL, NIL, (#:G0001), (SETQ X #:G0001), X 

要写入变量x,我们不需要任何临时存储,并且由于没有临时值,我们不需要任何表单来为它们生成值。我们在这里可以注意到,第一个和第二个值总是列表,它们应该始终具有相同的长度。第三个值是商店变量列表。这是一个列表,因为我们实际上可以使用setf来修改多个值,但在这种情况下只有一个。这里的变量是宏应该实际存储场所新值的位置。然后, writer-form (setq x #:g0001)实际上会将值放在该位置。当然,x是一种阅读价值的简单方法。

作为一个更复杂的例子,请看一下SBCL的成绩单:

CL-USER> (defstruct person
           person-name)
;⇒ PERSON

CL-USER> (get-setf-expansion '(char (person-name (first (second list-of-list-of-persons))) 3))
; (#:TMP965)
; ((PERSON-NAME (FIRST (SECOND LIST-OF-LIST-OF-PERSONS))))
; (#:NEW964)
; (SB-KERNEL:%CHARSET #:TMP965 3 #:NEW964)
; (CHAR #:TMP965 3)

这意味着如果我们想要更改人员列表中第二个人名单中第一个人姓名的第四个字符,我们可以这样做:

(let* ((temp965 (person-name (first (second list-of-list-of-persons))))
       (old-char (char tmp965 3)))        ; optional 
  (setq new964 <compute-new-value>)
  (sb-kernel:%charset tmp965 3 new964))

我们可以根据需要计算新值(只需填写<compute-new-value>),如果需要,我们甚至可以引用旧值(通过包含可选行)。我们需要做的就是将new964设置为新值,然后执行提供给我们的 writer-form

Stack Overflow上有更多get-setf-expansion的例子: