微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

修改 Lisp 中结构域/槽的副本

如何解决修改 Lisp 中结构域/槽的副本

我正在尝试修改结构字段的副本。我尝试使用 copY-TREE 无济于事。这是我的代码

(defstruct scenario
  (board '() :type list)
  (letters "" :type string)
  (blank-char #\- :type character))

(defparameter *scen-1*
  (make-scenario
    :board (string->board "cat---|a-----|b-----" #\|)))

SETF之前:

(print *scen-1*)

#S(SCENARIO
   :BOARD ("CAT---" "A-----" "B-----")
   :LETTERS ""
   :BLANK-CHAR #\-)

当我尝试使用 copY-TREE 修改电路板的副本时,它会修改原始电路板。

(let ((board (copy-tree (scenario-board *scen-1*))))
  (setf (subseq (nth 1 board) 0 2) "GG"))

(print *scen-1*)

#S(SCENARIO
   :BOARD ("CAT---" "GG----" "B-----")
   :LETTERS ""
   :BLANK-CHAR #\-)

*scen-1* 应该保持不变。

如何修改 BOARD 字段的副本,而不是原始字段?谢谢!

解决方法

问题是 COPY-TREE 复制了列表结构,而不是列表的元素。此处实际上并不需要 COPY-TREE,因为要复制的列表是扁平的。

一种解决方法是编写一个函数,用另一个字符串中的字符替换一个字符串副本中的字符,然后在原始 board 的副本中替换所需的 board 元素。这是一个执行子字符串替换的函数:

;;; Writes the characters from NEW to OLD starting from START.
(defun replace-substring (old start new)
  (let ((result (copy-seq old)))
    (loop for replacement across new
          for i from start below (+ start (length new))
          do (setf (elt result i) replacement)
          finally (return result))))

这是一个函数,它通过更新一个字符串元素的copy来更新 board 字段的copy

;;; Creates a copy of the BOARD field with the Nth string replaced
;;; by a copy which has had the characters starting from POS
;;; replaced by the characters from NEW.
(defun update-board (board n pos new)
  (let ((new-board (copy-list board))
        (new-seq (replace-substring (nth n board) pos new)))
    (setf (nth n new-board) new-seq)
    new-board))

示例交互:

CL-USER> *scen-1*
#S(SCENARIO :BOARD ("cat---" "a-----" "b-----") :LETTERS "" :BLANK-CHAR #\-)

CL-USER> (update-board (scenario-board *scen-1*) 1 0 "gg")
("cat---" "gg----" "b-----")

CL-USER> *scen-1*
#S(SCENARIO :BOARD ("cat---" "a-----" "b-----") :LETTERS "" :BLANK-CHAR #\-)
,

作为上述答案的替代方案,我发现 MAPCARCOPY-SEQ 可用于执行字符串列表的“深度复制”。

(let ((board (mapcar #'copy-seq (scenario-board *scen-1*))))
etc

这也能解决问题。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。