Перебор строк - PullRequest
       34

Перебор строк

0 голосов
/ 12 декабря 2018

У меня есть две строки одинаковой длины, которые отличаются ровно одним символом, и я хочу, чтобы строка всех символов была одинаковой.В общем, что-то вроде этого, которое вычисляется как строка, а не как список символов:

(loop for a across "abcd"
      for b across "abce"
      when (char= a b) collect a)

Хотя производительность здесь не проблема, я обнаружил, что вокруг нее стоит (coerce ... 'string).

Итак, я придумал что-то вроде

(loop with result = ""
      for a across "abcd"
      for b across "abce"
      when (char= a b)
        do (setf result (concatenate 'string result (string a)))
      finally (return result))

, которое делает работу, но выглядит для меня не очень элегантно.

(map 'string (lambda (a b) (when (char= a b) a)) "abcd" "abce")

выглядит лучше, но не работает, потому что NIL не является символом, когда a и b не равны.

Есть ли более элегантная идиома для повторениястроку и получить строку обратно?

Ответы [ 4 ]

0 голосов
/ 22 декабря 2018

Другой способ (он ничего не подразумевает относительно положения элементов)

    (defun only-matching (seq1 seq2)
      (remove-if-not (lambda (c) (find c seq1)) seq2))

    CL-USER> (only-matching "abcd" "abce")
    "abc"
    CL-USER> (only-matching "abdc" "abec")
    "abc"`

или

(coerce
 (intersection (coerce "abdc" 'list)
               (coerce "abec" 'list))
 'string)

, который также не сохраняет порядок

Примечание: remove-if-not устарела «официально».

0 голосов
/ 12 декабря 2018

Используйте map для одновременного зацикливания нескольких списков.

(map 'string #'(lambda (a b) (if (char= a b) a #\Rubout)) "abce" "abcd")

'string объединяет результирующий список в строку.#\Rubout get приведен к строке нулевой длины.#\Backspace даже удалит последний символ.

0 голосов
/ 12 декабря 2018

Другая возможность - использовать mismatch, как в комментарии Дэвида Ходжа:

CL-USER> (defun f(a b)
           (let ((pos (mismatch a b)))
             (concatenate 'string (subseq a 0 pos) (subseq a (1+ pos)))))
F
CL-USER> (f "abcdefg" "abcxefg")
"abcefg"
0 голосов
/ 12 декабря 2018
(loop with result = ""
      for a across "abcd"
      for b across "abce"
      when (char= a b)
        do (setf result (concatenate 'string result (string a)))
      finally (return result))

Повторное объединение не является хорошей идеей для более длинных строк.

Альтернативы:

Зацикливание в списке и приведение к строке

CL-USER 3 > (loop for a across "abcd"
                  and b across "abce"
                  when (char= a b) collect a into list
                  finally (return (coerce list 'string)))
"abc"

Использование потока и преобразование его в строку

CL-USER 4 > (with-output-to-string (*standard-output*)
              (loop for a across "abcd"
                    and b across "abce"
                    when (char= a b) do (write-char a)))
"abc"

Использование настраиваемой строки

CL-USER 5 > (loop with string = (make-array 0
                                            :element-type 'character
                                            :adjustable t
                                            :fill-pointer 0)
                  for a across "abcd"
                  for b across "abce"
                  when (char= a b) do (vector-push-extend a string)
                  finally (return string))
"abc"
...