关于onlisp中的广义变量 [英] About generalized variable in onlisp

查看:83
本文介绍了关于onlisp中的广义变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不确定这是怎么回事,这是文本中的一个宏示例. 基本上,我对如何使用内置宏(可能是函数) get-setf-method 不满意. 具体来说, get-setf-method 的某些返回值为nil的情况如何? 例如 (get-setf方法'x)

I am not sure what is going on here, a macro example in the text. Basically, not comfortable with how to use get-setf-method, a built-in macro (maybe function?). To be specific, how about the case that some of the return values of get-setf-method are nil? e.g. (get-setf-method 'x)

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

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

And why this example code set the fifth return value to the second return value first, for initialization? Finally how it can handle the order of setting the variables in an expression, such as (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))))

推荐答案

这实际上是一些较旧的代码.如问题SETF-METHOD-中所述,get-setf-method实际上已由get-setf-expansion代替. VS-SETF-METHOD撰写.因此,这几天您应该感兴趣的是 get-setf-expansion .它返回的值是将值安全存储在位置中所需的代码段.这非常重要,因为很容易错误地编写modyfing宏.

That's actually some older code. get-setf-method was actually replaced by get-setf-expansion as described in Issue SETF-METHOD-VS-SETF-METHOD Writeup. So what you should be interested in these days is get-setf-expansion. The values that it returns are the pieces of code that you need to safely store a value in a location. This is very important because it's very easy to write modyfing macros incorrectly.

关于为什么某些值可以是nil的原因,get-setf-expansion文档中的示例之一实际上显示了某些值可以是nil的情况:

As to why some of the values can be nil, one of the examples in the documentation for get-setf-expansion actually shows how some of the values can be nil:

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

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

But what are those values? For that we need to look at the syntax of the documentation:

语法:

获取setf扩展 放置&optional环境

Syntax:

get-setf-expansion place &optional environment

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

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

地点-一个地点.

环境-环境对象.

vars,vals,store-vars,writer-form,reader-form-setf扩展.

5.1.1.2 Setf扩展:

临时变量列表:列出临时变量的符号列表,这些临时变量就像通过let *顺序绑定到结果值 从价值形式.

List of temporary variables a list of symbols naming temporary variables to be bound sequentially, as if by let*, to values resulting from value forms.

值形式列表:形式列表(通常是场所的子形式),在评估时会得出这些值所对应的值 应绑定相应的临时变量.

List of value forms a list of forms (typically, subforms of the place) which when evaluated yield the values to which the corresponding temporary variables should be bound.

存储变量列表:列出临时存储变量的符号列表,这些临时存储变量用于保存将要分配给的新值 这个地方.

List of store variables a list of symbols naming temporary store variables which are to hold the new values that will be assigned to the place.

存储表单,该表单可以同时引用临时变量和商店变量,并且可以更改场所的价值和 保证返回存储变量的值作为其值, 是setf返回的正确值.

Storing form a form which can reference both the temporary and the store variables, and which changes the value of the place and guarantees to return as its values the values of the store variables, which are the correct values for setf to return.

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

Accessing form a form which can reference the temporary variables, and which returns the value of the place.

那么示例中的这些值意味着什么?

So what do those values in the example mean?

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

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

To write to variable x, we don't need any temporary storage, and since there are no temporary values, we don't need any forms to produce values for them. We can notice here that the first and second values are always lists, and they should always have the same length. The third value is a list of store variables. This is a list, because we can actually use setf to modify multiple values, but in this case there's just one. The variables here are where the macro should actually store the new values for the place. Then, it's the writer-form (setq x #:g0001) that will actually take care of putting the value in the place. x, of course, is a simple way of reading the value.

作为更复杂的示例,请看一下SBCL的抄本:

As a more complex example, have a look at this transcript from 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)

这意味着,如果我们要更改人员列表中第二个人列表中第一个人的名字的第四个字符,可以使用以下方法做到这一点:

This means that if we wanted to change the fourth character of the name of the first person in the second list of persons in a list of lists of persons, we could do it with:

(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 .

We can compute the new value however we want (just fill in for <compute-new-value>), and we can even reference the old value if we want to (by including the optional line). All we need to do is set new964 to the new value, and then execute the writer-form that was given to us.

关于堆栈溢出的get-setf-expansion的更多示例:

There are more examples of get-setf-expansion on Stack Overflow:

  • what is to append as push is to cons, in Lisp?
  • Writing a destructive macro or function like incf?
  • Writing a ++ macro in Common Lisp
  • How could I implement the push macro?

这篇关于关于onlisp中的广义变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆