如何修改键为字符串的嵌套plist并生成值修改后的等同新变量
解决方案
1. 直接修改原plist
嵌套使用getf定位到目标值后直接赋值即可,getf本身支持setf修改对应位置的值:
(setf (getf (getf (getf response "person") "child-1") "grandchild-1b") "grandchild-1b-value-modified") ;; 操作完成后可直接返回response response
该方案会直接修改解析得到的response对象,不需要额外拷贝开销。
2. 生成新plist不修改原对象
推荐直接对原plist做深拷贝后修改副本,Common Lisp生态常用的Alexandria工具库提供了开箱即用的copy-tree函数实现深拷贝:
(let ((modified-response (alexandria:copy-tree response))) (setf (getf (getf (getf modified-response "person") "child-1") "grandchild-1b") "grandchild-1b-value-modified") ;; 返回修改后的新plist,原response完全不受影响 modified-response)
如果不想引入第三方依赖,也可以逐层复制需要修改的plist层级,仅修改目标路径的结构,其余部分和原对象共享内存,性能更高:
(let* ((root-person (getf response "person")) (child-1 (getf root-person "child-1")) ;; 复制孙层级并替换目标键值 (new-child-1 (loop for (k v) on child-1 by #'cddr if (string= k "grandchild-1b") collect k and collect "grandchild-1b-value-modified" else collect k and collect v)) ;; 复制子层级并替换child-1的值 (new-root-person (loop for (k v) on root-person by #'cddr if (string= k "child-1") collect k and collect new-child-1 else collect k and collect v)) ;; 复制顶层plist并替换person的值 (new-response (loop for (k v) on response by #'cddr if (string= k "person") collect k and collect new-root-person else collect k and collect v))) new-response)
填写到示例代码的最终效果(直接修改原对象版本)
(let ((stream (drakma:http-request endpoint :method :post :want-stream t :content-type "application/json" :content request))) (setf (flexi-streams:flexi-stream-external-format stream) :utf-8) (let ((response (yason:parse stream :object-as :plist))) (setf (getf (getf (getf response "person") "child-1") "grandchild-1b") "grandchild-1b-value-modified") response))
内容的提问来源于stack exchange,提问作者user3735178
相关产品推荐
相关产品推荐

