在Lisp中,要添加的内容与否是要添加的内容是什么? [英] what is to append as push is to cons, in Lisp?

查看:84
本文介绍了在Lisp中,要添加的内容与否是要添加的内容是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(push x list)

扩展到

(setq list (cons x list))

内容扩展为以下内容:

(setq list (append list2 list))

?有标准的宏吗?

推荐答案

正如其他答案和评论所指出的那样,没有标准宏,您可以编写自己的宏.我认为,这对于> c0> ,我将首先对其进行描述.您还可以使用 get-setf-expansion 手动编写这样的宏,我将显示一个例子.

As other answers and comments have pointed out, there's not a standard macro for this, and you can write your own. In my opinion, this is a good case for define-modify-macro, and I'll describe that first. You can also write such a macro manually, using get-setf-expansion, and I'll show an example of that, too.

HyperSpec页面上define-modify-macro的示例之一是appendf:

One of the examples on the HyperSpec page for define-modify-macro is appendf:

说明:

define-modify-macro定义了一个名为name的宏,用于读写位置.

Description:

define-modify-macro defines a macro named name to read and write a place.

新宏的参数是一个地方,后跟lambda-list中提供的参数.使用define-modify-macro定义的宏可以正确地将环境参数传递给get-setf-expansion.

The arguments to the new macro are a place, followed by the arguments that are supplied in lambda-list. Macros defined with define-modify-macro correctly pass the environment parameter to get-setf-expansion.

调用宏时,会将函数应用于场所的旧内容和lambda-list参数以获得新值,并更新场所以包含结果.

When the macro is invoked, function is applied to the old contents of the place and the lambda-list arguments to obtain the new value, and the place is updated to contain the result.

(define-modify-macro appendf (&rest args) 
   append "Append onto list") =>  APPENDF
(setq x '(a b c) y x) =>  (A B C)
(appendf x '(d e f) '(1 2 3)) =>  (A B C D E F 1 2 3)
x =>  (A B C D E F 1 2 3)
y =>  (A B C)

示例中的appendf与您要查找的内容相反,因为额外的参数作为place参数的尾部附加在后面.但是,我们可以编写所需行为的功能版本(只是append,其中参数顺序已互换),然后使用define-modify-macro:

The appendf in the example is reversed from what you're looking for, since the extra arguments are appended as the tail of the place argument. However, we can write the functional version of the desired behavior (it's just append with argument order swapped), and then use define-modify-macro:

(defun swapped-append (tail head)
  (append head tail))

(define-modify-macro swapped-appendf (&rest args)
  swapped-append)

(let ((x '(1 2 3))
      (y '(4 5 6)))
  (swapped-appendf x y)
  x)
; => (4 5 6 1 2 3)

如果您不想将swapped-append定义为函数,则可以为define-modify-macro提供lambda表达式:

If you don't want to define swapped-append as a function, you can give a lambda-expression to define-modify-macro:

(define-modify-macro swapped-appendf (&rest args)
  (lambda (tail head) 
    (append head tail)))

(let ((x '(1 2 3))
      (y '(4 5 6)))
  (swapped-appendf x y)
  x)
; => (4 5 6 1 2 3)

因此,答案是,从概念上讲,(swapped-appendf list list2)扩展为(setq list (append list2 list)).仍然是swapped-appendf的参数似乎顺序错误的情况.毕竟,如果我们使用define-modify-macrocons定义了push,则参数的顺序将与标准push的顺序不同:

So, the answer is that, conceptually, (swapped-appendf list list2) expands to (setq list (append list2 list)). It's still the case that the arguments to swapped-appendf may seem to be in the wrong order. After all, if we defined push using define-modify-macro and cons, the arguments would be in a different order from the standard push:

(define-modify-macro new-push (&rest args)
  (lambda (list item)
    (cons item list)))

(let ((x '(1 2 3)))
  (new-push x 4)
  x)
; => (4 1 2 3)

define-modify-macro是一个方便的工具,我发现它在易于编写功能性(即非副作用)版本的功能并且还需要API的修改版本时非常有用.

define-modify-macro is a handy tool to know about, and I've found it useful when functional (i.e., non-side-effecting) versions of functions are easy to write and a modifying version is also desired for an API.

new-push的参数是listitem,而push的参数是itemlist.我认为swapped-appendf中的参数顺序不是那么重要,因为它不是标准的习惯用法.但是,可以编写一个prependf宏来实现其他顺序,该宏的实现使用 get-setf-expansion 安全地获取该地点的 Setf扩展,并且避免进行多次评估.

new-push's arguments are list and item, whereas push's arguments are item and list. I don't think the argument order in swapped-appendf is quite as important, since it's not a standard idiom. However, it is possible to achieve the other order by writing a prependf macro whose implementation uses get-setf-expansion to safely get the Setf Expansion for the place, and to avoid multiple evaluation.

(defmacro prependf (list place &environment environment)
  "Store the value of (append list place) into place."
  (let ((list-var (gensym (string '#:list-))))
    (multiple-value-bind (vars vals store-vars writer-form reader-form)
        (get-setf-expansion place environment)
      ;; prependf works only on a single place, so there
      ;; should be a single store-var.  This means we don't
      ;; handle, e.g., (prependf '(1 2 3) (values list1 list2))
      (destructuring-bind (store-var) store-vars
        ;; Evaluate the list form (since its the first argument) and
        ;; then bind all the temporary variables to the corresponding
        ;; value forms, and get the initial value of the place.
        `(let* ((,list-var ,list)
                ,@(mapcar #'list vars vals)
                (,store-var ,reader-form))
           (prog1 (setq ,store-var (append ,list-var ,store-var))
             ,writer-form))))))

(let ((x '(1 2 3))
      (y '(4 5 6)))
  (prependf y x)
  x)
; => (4 5 6 1 2 3)

使用get-setf-expansion意味着该宏也可以在更复杂的地方使用:

The use of get-setf-expansion means that this macro works on more complicated places, too:

(let ((x (list 1 2 3))
      (y (list 4 5 6)))
  (prependf y (cddr x))
  x)
; => (1 2 4 5 6 3)

出于教育目的,有趣的是看到相关的宏扩展,以及它们如何避免对表单进行多次求值,以及writer-form是什么用于实际设置值. get-setf-expansion捆绑了很多功能,其中一些是特定于实现的:

For educational purposes, it's interesting to see the relevant macroexpansions, and how they avoid multiple evaluations of the forms, and what the writer-forms are that are used to actually set the value. There's a lot of functionality bundled into get-setf-expansion, and some of it is implementation specific:

;; lexical variables just use SETQ
CL-USER> (pprint (macroexpand-1 '(prependf y x)))
(LET* ((#:LIST-885 Y)
       (#:NEW886 X))
  (PROG1 (SETQ #:NEW886 (APPEND #:LIST-885 #:NEW886))
    (SETQ X #:NEW886)))

;; (CDDR X) gets an SBCL internal RPLACD
CL-USER> (pprint (macroexpand-1 '(prependf y (cddr x))))
(LET* ((#:LIST-882 Y)
       (#:G883 X)
       (#:G884 (CDDR #:G883)))
  (PROG1 (SETQ #:G884 (APPEND #:LIST-882 #:G884))
    (SB-KERNEL:%RPLACD (CDR #:G883) #:G884)))

;; Setting in an array gets another SBCL internal ASET function
CL-USER> (pprint (macroexpand-1 '(prependf y (aref some-array i j))))
(LET* ((#:LIST-887 Y)
       (#:TMP891 SOME-ARRAY)
       (#:TMP890 I)
       (#:TMP889 J)
       (#:NEW888 (AREF #:TMP891 #:TMP890 #:TMP889)))
  (PROG1 (SETQ #:NEW888 (APPEND #:LIST-887 #:NEW888))
    (SB-KERNEL:%ASET #:TMP891 #:TMP890 #:TMP889 #:NEW888)))

这篇关于在Lisp中,要添加的内容与否是要添加的内容是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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