Глибока копія рядка в Елісп?


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

1
Гаразд, повідомив про помилку на debbugs.gnu.org/cgi/bugreport.cgi?bug=20153
abo-abo

Дякуємо, що повідомили про помилку. Шкода, що ніхто ще не відповів на це. Було б добре зафіксувати цю корисну функцію (закодовану в С).
Дрю

Відповіді:


7

Ви можете використовувати функцію font-lock-append-text-propertyдля додавання властивості тексту. Це не змінює значення деструктивно.

Наприклад:

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

У моїх тестах це було приблизно на 20% швидше, ніж ваше readрішення. Я також написав версію, яка не використовувала тимчасовий буфер і змінила властивості рядка, який був меншим кодом, але був повільнішим.

Дивлячись на код C, він копіює списки властивостей, з copy_sequence, що відновить структуру списку, але не копіює елементи за значенням, тому властивості, як обличчя у вашому прикладі, що мають значення списку, копіюються за допомогою посилання та змінюються. Помилка чи ні, я не знаю


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
Не працює, я додам приклад.
або-або

1
Трюк полягає в тому, щоб мати вкладений список у властивостях, як я. Тоді concatне працює.
або-або

@ або-або. Гаразд, тепер я бачу. Я цього не помітив у вашому додатковому прикладі. У цьому випадку у мене немає відповіді, але я думаю, що існує реальна потреба в такій функції. (Однією з потенційних проблем є те, що неможливо дізнатися, чи може невідома власність посилатися на якийсь спільний об'єкт.)
Lindydancer

1

Знайдено (не дуже ефективний) обхід:

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

2
Обхід завершується невдачею, якщо пропептики містять #символ.
або-або

ти маєш на увазі, якщо символ # є частиною назви символу? Або маються на увазі властивості, які буфери або інші недруковані дані? Якщо це перший, вам слід подати помилку.
Малабарба

буфери за властивостями
або-або
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.