【问题标题】:About generalized variable in onlisp关于 onlisp 中的广义变量
【发布时间】:2014-02-19 20:12:25
【问题描述】:

我不确定这里发生了什么,文本中的一个宏示例。 基本上,不习惯如何使用 get-setf-method,一个内置宏(也许是函数?)。 具体来说,get-setf-method的一些返回值是nil的情况如何? 例如 (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))))

【问题讨论】:

  • 一句警告:如果您不将环境参数传递给 g-s-e,您的宏可能会设置错误的位置。将 &environment env 添加到包含宏的 lambda 列表中,然后将 env 作为第二个参数传递给 g-s-e。 &environment 参数不会改变宏的调用方式。在 On Lisp 中更容易理解的 g-s-e 示例是 _F

标签: lisp common-lisp


【解决方案1】:

这实际上是一些较旧的代码。 get-setf-method 实际上已替换为 get-setf-expansion,如 Issue SETF-METHOD-VS-SETF-METHOD Writeup 中所述。所以这些天你应该感兴趣的是get-setf-expansion。它返回的值是将值安全地存储在某个位置所需的代码片段。这一点非常重要,因为很容易错误地编写修改宏。

至于为什么某些值可以是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——一个 setf 扩展。

这五个返回值在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))

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-14
    • 1970-01-01
    • 2011-02-03
    • 2013-03-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多