Common lisp Common Lisp如何推送到返回的列表?
假设我有一个列表变量Common lisp Common Lisp如何推送到返回的列表?,common-lisp,side-effects,Common Lisp,Side Effects,假设我有一个列表变量*test*设置为(:v1(1):v2(2)) 然后,在一些字符串配对之后,我需要将另一个1添加到:v1,相当于: (push 1 (getf *test* :v1)) (push 1 (getf-string-equal *test* "v1")) 但是,它看起来更像: (push 1 (getf-string-equal *test* "v1")) 其中getf字符串等于(取自) 然而,我的问题是,我不能在返回的列表上使用setf。我可以使用一些难看的技巧来推动函数
*test*
设置为(:v1(1):v2(2))
然后,在一些字符串配对之后,我需要将另一个1
添加到:v1
,相当于:
(push 1 (getf *test* :v1))
(push 1 (getf-string-equal *test* "v1"))
但是,它看起来更像:
(push 1 (getf-string-equal *test* "v1"))
其中getf字符串等于(取自)
然而,我的问题是,我不能在返回的列表上使用setf
。我可以使用一些难看的技巧来推动函数内部,附带副作用,但我正试图避免这种情况
如何修改通过以字符串形式搜索属性而获得的列表属性?相当于:
(push 1 (getf *test* :v1))
(push 1 (getf-string-equal *test* "v1"))
谢谢。注意:这个答案不是一个完整的解决方案,因为它不能处理找不到钥匙的情况。这只是给你指出正确的方向
这个问题有几个层次。首先,push
是一个或多或少扩展为setf
调用的宏。因此,我们需要为getf string equal
定义一个setf
扩展。这很容易做到,只需一点复杂
(defsetf getf-string-equal (plist indicator) (value)
(let ((i (gensym))
(rest (gensym))
(match (gensym)))
`(let ((,match (loop
for (,i . ,rest) on ,plist by #'cddr
when (string-equal ,i ,indicator)
return ,rest)))
(if ,match
(setf (car ,match) ,value)
nil))))
让我们把它分解一下。我们使用长格式的defsetf
来定义函数的扩展plist
和indicator
是参数,与前面相同,value
是要分配的新值。但这不是一个函数;它更接近于宏。所以我们要生成一些代码。为此,我们需要一些gensym
调用来获取一些临时变量
接下来,我们生成运行与访问器中使用的相同循环的代码。这里的区别是,我们将返回包含它的cons单元格,而不是返回实际值。这样,我们就可以在该单元格中设置car
,并实际修改数据结构。如果找到匹配项,我们就这样做,我们就完蛋了
但是,如果没有找到匹配项,则会出现问题。如果没有这样的
cons
单元格,我们就不可能设置它的car
。这是本if
中的第二种情况。我们只想扩展到一个`(setf,plist,value)
,它将具有正确的语义。但是,defsetf
不允许我们像宏那样访问实际使用的变量。因此,为了完全解决这个问题,包括找不到值的拐角情况,我们必须深入研究defsetf
的完全通用形式。为了让您体验一下这个宏的通用性,为了使用它正确地编写一个setf
扩展,您的扩展必须是一个返回五个不同值的表达式。为什么不直接使用(intern(string upcase“v1”)“KEYWORD”)
?因为我不知道我能做到:-)我会试试这个好把戏。虽然我最终在Sylwester评论中使用了这个技巧,但我还是接受了,因为它展示了如何解决问题,这反过来又解释了如何解决一类类似的问题。是的,如果你能避免黑洞,即setf
扩展,我强烈建议你这样做。我认为,每次我必须进行setf
扩展时,我必须查找defsetf
宏,并提醒自己其参数的工作方式。希望这个答案能帮助其他人解决类似的问题