Выполнение нескольких замен строк с использованием индексов

Я использую следующий рецепт поваренной книги для замены неуникальных подстрок в тексте:

 (defn string-splice
    "cookbook recipe: http://gettingclojure.wikidot.com/cookbook:strings
     Given three arguments, string-splice will replace a portion of the old string at the       
     given offset equal to the length  of the replacement. The resulting string will be the      
     same  length as the original. The optional fourth argument 
     specifies the length of text to be replaced. If this argument length is greater than the    
     length of the new string, then the result will be shorter than the original string."

     ([target new offset] (string-splice target new offset (count new)))
     ([target new offset length]
     (str  (subs target 0 offset)   new (subs target (+ offset length))  )   ) )

Теперь предположим, что у меня есть следующая строка с ошибкой

 (def bad-st "mary had a littl lam whose fleec was whiteas snw.")

и следующий список исправлений с соответствующим индексом, указывающим, где слово с ошибкой встречается в bad-st:

 (def corrections '(Mary 0 Little 11 fleck 27 white as 37 Snow 45))

Если я хочу кумулятивно подставить каждое из этих исправлений в строку, а также сместить символы в строке, чтобы учесть исправление, которое длиннее или короче, чем подстрока с ошибкой, я могу использовать версию кода сокращения, указанную для связанная проблема.

 (reduce (fn [st [x y ]]
      (string-splice  st x y (count x) )) 
            bad-st
         (partition 2 corrections))

Однако это не приводит к правильному сдвигу символов в исходном тексте. Выход

 "Mary had a Littlelam whose fleck was white asSnow"

Может ли кто-нибудь сказать мне, что я делаю неправильно, и предложить исправление?


person Renklauf    schedule 13.06.2014    source источник


Ответы (1)


Основная проблема с вашим использованием string-splice заключается в том, что вы передаете неверный четвертый аргумент, который должен быть длиной замененной подстроки - вы передаете длину замены. Итак, вам нужно найти длину плохого слова в позиции исправления.

(defn wsize-at 
  "size of word (non-white sequence) at position n in string s"
  [n s]
  (let [[head tail] (split-at n s)]
    (count (take-while #(not (Character/isWhitespace %)) tail))))

Проблема, возникающая при использовании сокращения, заключается в том, что это сбрасывает индексы позже в строке, если заменяемая и заменяемая строки имеют разную длину. Вы можете обойти это, работая с конца строки назад:

(reduce (fn [st [s n]] (string-splice st s n (wsize-at n st)))
  bad-st 
  (reverse (partition 2 corrections)))

Я не уверен, что string-splice подходит для этой задачи. Смещения поправок указаны в исходной строке; другой подход состоял бы в том, чтобы использовать эти смещения для извлечения неизмененных сегментов исходной строки, скажем, с помощью функции good-parts, такой, что (good-parts bad-st [0 11 27 37 45]) дает [" has a ", " lam who ", " was ", " snw."] -- wsize-at будет частью реализации. Затем вы чередуете это с ["Mary", "Little", "fleck", "white as", "Snow"] и применяете str к результату, чтобы получить желаемую строку.

person Randy Hudson    schedule 14.06.2014
comment
Отличное исправление. На самом деле я экспериментировал с вашим предложением о чередовании прошлой ночью (до того, как вы расширили свой пост), но это даже лучше. - person Renklauf; 14.06.2014