gpt4 book ai didi

common-lisp - Common Lisp - 如何使用关键字参数调用/应用函数?

转载 作者:行者123 更新时间:2023-12-03 04:51:37 25 4
gpt4 key购买 nike

上下文

  1. 使用像 (lambda (List arg1 arg2 ... argn)) 这样的函数,我可以使用funcall/apply 使用原始参数调用这些方法,从而修改列表在 lambda 内部。
  2. 对于像 (lambda (arg1 arg2 ... argn &key List)) 这样的函数,我只能将 funcall/apply 与以下副本一起使用这参数,这意味着我无法在函数内修改它们。
  3. 如何使用 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) 。在这两种情况下,原始列表都未修改,但您会得到一个新的参数列表(可能共享最后一个列表,但这是一个细节)。

但是,输入列表和结果列表的内容相同,在串联之前和之后在这些列表中引用完全相同的对象,不存在对象的隐式副本。 p>

让我们看看:

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

注意。未定义的行为是改变参数列表本身。

<小时/>

编辑:

也许您确实将内存本身与参数连接起来,在这种情况下,在被调用函数中使用了代表内存的新列表,但如果是这样,这是一个错误,因为连接仅应该修改参数列表,不是参数之一。

关于common-lisp - Common Lisp - 如何使用关键字参数调用/应用函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60998273/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com