在Elisp中深度复制字符串?


9

我有一个适当的字符串。我想对其进行深拷贝,以添加更多属性,同时将属性保留在原始字符串中。我该怎么做(轻松)?

一对一评估:

(setq test-str-1
      #(";; This `is' a test"
        0 3 (fontified nil face font-lock-comment-delimiter-face)
        3 9 (fontified nil face font-lock-comment-face)
        9 11 (fontified nil face (font-lock-constant-face font-lock-comment-face))
        11 19 (fontified nil face font-lock-comment-face)))
(setq test-str-2 (concat test-str-1))
(add-face-text-property 0 (length test-str-2) 'foobar t test-str-2)

结果:

test-str-2
;; =>
#(";; This `is' a test" 0 3 (fontified nil face (font-lock-comment-delimiter-face foobar))
  3 9 (fontified nil face (font-lock-comment-face foobar))
  9 11 (fontified nil face (font-lock-constant-face font-lock-comment-face foobar))
  11 19 (fontified nil face (font-lock-comment-face foobar)))
test-str-1
;; =>
#(";; This `is' a test" 0 3 (face font-lock-comment-delimiter-face fontified nil)
  3 9 (face font-lock-comment-face fontified nil)
  9 11 (face (font-lock-constant-face font-lock-comment-face foobar) ; <= foobar is here
        fontified nil)
  11 19 (face font-lock-comment-face fontified nil))

2
我会将其报告为中的错误add-face-text-property。它不应该破坏性地修改列表,因为当其他人引用该列表时,它会失败。
Lindydancer


感谢您报告错误。太糟糕了,还没有人对此做出回应。修复此实用程序功能(用C编码)会很好。
提请

Answers:


7

您可以使用该函数font-lock-append-text-property添加text属性。它不会破坏性地修改该值。

例如:

(setq test-str-1
      #(";; This `is' a test"
        0 3 (fontified nil face font-lock-comment-delimiter-face)
        3 9 (fontified nil face font-lock-comment-face)
        9 11 (fontified nil face (font-lock-constant-face font-lock-comment-face))
        11 19 (fontified nil face font-lock-comment-face)))
(setq test-str-2 (concat test-str-1))
(font-lock-append-text-property 0 (length test-str-2) 'face '(foobar t) test-str-2)


test-str-1
#(";; This `is' a test"
  0 3 (face font-lock-comment-delimiter-face fontified nil)
  3 9 (face font-lock-comment-face fontified nil)
  9 11 (face (font-lock-constant-face font-lock-comment-face) fontified nil)
  11 19 (face font-lock-comment-face fontified nil))

test-str-2
#(";; This `is' a test"
  0 3 (fontified nil face (font-lock-comment-delimiter-face foobar t))
  3 9 (fontified nil face (font-lock-comment-face foobar t))
  9 11 (fontified nil face (font-lock-constant-face font-lock-comment-face foobar t))
  11 19 (fontified nil face (font-lock-comment-face foobar t)))

在此,中的test-str-1保留了其原始值。


4

我发现可以通过遍历文本属性,复制基础属性数据并用新副本覆盖现有属性来做到这一点。

(defun deep-copy-text-properties (str)
  (with-temp-buffer
    (insert str)
    (goto-char 1)
    (while (not (eobp))
      (set-text-properties (point)
                           (goto-char (next-char-property-change (point) (point-max)))
                           ;; copy-tree is the important part
                           (copy-tree (text-properties-at (1- (point))))))
    (buffer-string)))

在我的测试中,这比您的read解决方案快20%。我还编写了一个不使用临时缓冲区的版本,并修改了代码较少但速度较慢的字符串属性。

查看C代码,它会复制属性plists,并使用copy_sequence来重建列表结构,但不会按值复制元素,因此示例中的face等具有列表值的属性将通过引用复制并修改。是否有错误,我不知道


2

您可以使用(concat the-original-string)

例如:

(let ((s "TEXT"))
  (set-text-properties 2 3 '(:foreground "blue") s)
  (let ((q (concat s)))
    (add-text-properties 2 3 '(:background "red") q)
    (cons s q)))
;; Returns:
(#("TEXT" 2 3 (:foreground "blue")) . #("TEXT" 2 3 (:foreground "blue" :background "red")))

1
不起作用,我将添加一个示例。
ABO血型ABO

1
诀窍是像我一样在属性中具有嵌套列表。然后concat不起作用。
ABO血型ABO

@ abo-abo。好的,现在我明白了。在您添加的示例中,我没有发现这一点。在那种情况下,我没有答案,但我认为确实需要这种功能。(一个潜在的问题是,无法知道未知属性是否可能指向某种共享对象。)
Lindydancer 2015年

1

找到了一个(不是很有效的)解决方法:

(setq test-str-2
      (read (prin1-to-string test-str-1)))

2
如果属性包含#字符,则解决方法将失败。
ABO血型ABO

您是说#字符是符号名称的一部分吗?还是意味着属性是缓冲区或其他不可打印的数据?如果是第一次,则应提交一个错误。
马拉巴巴

属性中的缓冲区
abo-abo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.