Common Lisp - 如何 funcall/apply 带有关键字参数的函数?
Common Lisp - How to funcall/apply a function with keyword arguments?
上下文
- 像
(lambda (List arg1 arg2 ... argn))
这样的函数我可以使用
funcall
/apply
使用原始参数调用这些方法,从而修改列表
在 lambda 里面。
- 对于像
(lambda (arg1 arg2 ... argn &key List))
这样的函数,我只能将 funcall
/apply
与
参数,这意味着
我不能在函数内部修改它们。
- 我怎样才能使用 2. 中的功能与 1. 中的功能相同?
问题详细
1。有效的函数
与(lambda (mem arg1 arg2 ... argn))
:
;; Can pass the original lists for modification inside the function:
(funcall #'fn program-memory args-list)
函数可以修改这些列表。
2。失去修改参数能力的函数
使用(lambda (arg1 arg2 ... argn &key mem))
,我只能用原始列表的副本调用它:
;; can only pass copies of the lists :(
(apply #'fn (concatenate 'list args (list :mem program-memory)))
这样我就不能再修改程序内存了
3。如何使 2. 中的功能像 1. 中的功能一样工作?
我怎样才能让它发挥作用?即,使用原始列表而不是副本调用函数。
带有简化旧代码的示例(如 1.):
(defun mem/w (memory address value)
"Writes the value to memory at address. Returns nil."
(setf (elt memory address) value)
nil)
;; sum
(defun sum-op (mem a b o)
(mem/w mem o (+ a b)))
(let ((program (list 1 2 3 4 5 6 7 8))
(args (list 1 2 0)))
(apply #'sum-op
(cons program args))
(print program)) ;; shows modification --> good
完整代码位于 https://github.com/AlbertoEAF/advent_of_code_2019/blob/master/common-lisp/day5.lisp。
关于调用时发生的情况似乎存在误解:
(concatenate 'list args (list :mem program-memory))
参数列表 args
和 (list :mem program-memory)
用于构建新列表。在这里你可以使用 append
,像这样:(append args (list :mem program-memory)
。在这两种情况下,原始列表都没有修改,但您会得到一个新的参数列表(可能与最后一个列表相同,但这是一个细节)。
但是,输入列表和结果列表的内容完全相同,串联前后这些列表中引用的对象完全相同,没有隐式复制对象。
让我们看看:
(defclass something () ())
(defvar *something* (make-instance 'something))
当我评估 *something*
时,生成的对象打印为 #<SOMETHING {10091B1973}>
(打印的表示可能因实现而异;对象的标识会不同)。
如果我把它放在一个列表中,然后调用 copy-list
,结果列表仍然包含相同的值:
(let ((list (list *something*)))
(assert (eq (first list)
(first (copy-list list)))))
如果您将列表存储在列表中,这同样适用,如果没有显式调用复制,它们将不会被递归复制。事实上,让我们尝试您给出的相同示例,但使用关键字:
;; unrelated, but for this Advent of Code you are going to have
;; performance issues if you treat memory as a list, and not a vector.
;; Here I change it to a vector.
(defun mem/w (memory address value)
"Writes the value to memory at address"
(setf (svref memory address) value))
;; mem is now a keyword argument
(defun sum-op (a b o &key mem)
(mem/w mem o (+ a b)))
(let ((memory (vector 0 2 3 0 0 0 0 0))
(args (list 1 2 0)))
;; using the backquote/slice syntax for brevity
;; but the result is like concatenate/append
(apply #'sum-op `(,@args :mem ,memory))
memory)
生成的内存状态为:
#(3 2 3 0 0 0 0 0)
注意。什么是未定义的行为是改变参数列表本身。
编辑:
也许您确实将内存本身与 args 连接在一起,在这种情况下,在被调用函数中使用了一个表示内存的新列表,但如果是这样,这是一个错误,因为连接只应该修改列表参数,而不是参数之一。
上下文
- 像
(lambda (List arg1 arg2 ... argn))
这样的函数我可以使用funcall
/apply
使用原始参数调用这些方法,从而修改列表 在 lambda 里面。 - 对于像
(lambda (arg1 arg2 ... argn &key List))
这样的函数,我只能将funcall
/apply
与 参数,这意味着 我不能在函数内部修改它们。 - 我怎样才能使用 2. 中的功能与 1. 中的功能相同?
问题详细
1。有效的函数
与(lambda (mem arg1 arg2 ... argn))
:
;; Can pass the original lists for modification inside the function:
(funcall #'fn program-memory args-list)
函数可以修改这些列表。
2。失去修改参数能力的函数
使用(lambda (arg1 arg2 ... argn &key mem))
,我只能用原始列表的副本调用它:
;; can only pass copies of the lists :(
(apply #'fn (concatenate 'list args (list :mem program-memory)))
这样我就不能再修改程序内存了
3。如何使 2. 中的功能像 1. 中的功能一样工作?
我怎样才能让它发挥作用?即,使用原始列表而不是副本调用函数。
带有简化旧代码的示例(如 1.):
(defun mem/w (memory address value)
"Writes the value to memory at address. Returns nil."
(setf (elt memory address) value)
nil)
;; sum
(defun sum-op (mem a b o)
(mem/w mem o (+ a b)))
(let ((program (list 1 2 3 4 5 6 7 8))
(args (list 1 2 0)))
(apply #'sum-op
(cons program args))
(print program)) ;; shows modification --> good
完整代码位于 https://github.com/AlbertoEAF/advent_of_code_2019/blob/master/common-lisp/day5.lisp。
关于调用时发生的情况似乎存在误解:
(concatenate 'list args (list :mem program-memory))
参数列表 args
和 (list :mem program-memory)
用于构建新列表。在这里你可以使用 append
,像这样:(append args (list :mem program-memory)
。在这两种情况下,原始列表都没有修改,但您会得到一个新的参数列表(可能与最后一个列表相同,但这是一个细节)。
但是,输入列表和结果列表的内容完全相同,串联前后这些列表中引用的对象完全相同,没有隐式复制对象。
让我们看看:
(defclass something () ())
(defvar *something* (make-instance 'something))
当我评估 *something*
时,生成的对象打印为 #<SOMETHING {10091B1973}>
(打印的表示可能因实现而异;对象的标识会不同)。
如果我把它放在一个列表中,然后调用 copy-list
,结果列表仍然包含相同的值:
(let ((list (list *something*)))
(assert (eq (first list)
(first (copy-list list)))))
如果您将列表存储在列表中,这同样适用,如果没有显式调用复制,它们将不会被递归复制。事实上,让我们尝试您给出的相同示例,但使用关键字:
;; unrelated, but for this Advent of Code you are going to have
;; performance issues if you treat memory as a list, and not a vector.
;; Here I change it to a vector.
(defun mem/w (memory address value)
"Writes the value to memory at address"
(setf (svref memory address) value))
;; mem is now a keyword argument
(defun sum-op (a b o &key mem)
(mem/w mem o (+ a b)))
(let ((memory (vector 0 2 3 0 0 0 0 0))
(args (list 1 2 0)))
;; using the backquote/slice syntax for brevity
;; but the result is like concatenate/append
(apply #'sum-op `(,@args :mem ,memory))
memory)
生成的内存状态为:
#(3 2 3 0 0 0 0 0)
注意。什么是未定义的行为是改变参数列表本身。
编辑:
也许您确实将内存本身与 args 连接在一起,在这种情况下,在被调用函数中使用了一个表示内存的新列表,但如果是这样,这是一个错误,因为连接只应该修改列表参数,而不是参数之一。