Замена символов в строке набором символов внутри карты в Clojure

У меня есть функция, которая принимает строку s и карту символов charmap. Если какие-либо символы в строке s находятся внутри charmap, замените символ значением карты.

Примечание, ключи в карте должны быть строкой, а не символом.

Например:

(replace-characters "Hi!" {"!" "Exclamation Mark"}) ;; => "HiExclamation Mark"

Это код, который я сейчас использую:

(defn- replace-characters
  "Replaces any characters in a string that are mapped in the given charmap"
  [s charmap]
  (let [character-set (set s)
        characters (filter #(contains? charmap (str %)) character-set)]
    (string/replace s (re-pattern (string/join "|" characters)) charmap)))

Тем не менее, я получаю NullPointerException, и я серьезно не понимаю, почему.

java.lang.NullPointerException: Cannot invoke "String.indexOf(int)" because "s" is null

Я хотел бы решить это в чистом Clojure, а не в Java и т. д.

Обновлять: Таким образом, приведенный выше код работает в repl. Что приятно. Но по какой-то причине следующее вызывает ошибку:

(-> s
     (string/replace #"[:/?#\[\]@!$&'()*+,;=]" "")
     (replace-characters charmap)) ;; Where charmap is a large map of key value characters.
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
Четыре эффективных способа центрирования блочных элементов в CSS
Четыре эффективных способа центрирования блочных элементов в CSS
У каждого из нас бывали случаи, когда нам нужно отцентрировать блочный элемент, но мы не знаем, как это сделать. Даже если мы реализуем какой-то...
3
0
61
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Это выражение, которое вызывает ошибку:

(str/replace "Hi" #"" {"!" "Exclamation Mark"})

("!" был заменен регулярным выражением на "", character-set имеет значение #{\H \i}, а characters — это (), поэтому шаблон, созданный с помощью re-pattern, равен #"".)

Пустое регулярное выражение соответствует каждому пробелу между буквами:

(str/replace "Hi" #"" " ")

=> " H i "

Итак, replace ищет замену в hash-map {"!" "Exclamation Mark"}, но ничего не находит - ключа нет "":

(str/replace "Hi" #"" {"!" "Exclamation Mark"})
=> error

(str/replace "Hi" #"" {"" " "})
=> " H i "

Одним из возможных решений может быть упрощение определения replace-characters (это решение будет работать только для непустого charmap):

(defn replace-characters [s charmap]
  (str/replace s (re-pattern (str/join "|" (keys charmap)))
               charmap))

Тесты:

(replace-characters "Hi!" {"!" "Exclamation Mark"})

=> "HiExclamation Mark"

(-> "Hi!"
    (str/replace #"[:/?#\[\]@!$&'()*+,;=]" "")
    (replace-characters {"!" "Exclamation Mark"}))

=> "Hi"

я бы просто пошел с чем-то таким простым, как это:

(apply str (replace {"!" "[exclamation mark]"
                         "?" "[question mark]"}
                       (map str "is it possible? sure!")))

;;=> "is it possible[question mark] sure[exclamation mark]"

или так с преобразователями:

(apply str (eduction (map str) (replace {"!" "[exclamation mark]"
                                                "?" "[question mark]"})
                        "is it possible? sure!"))

или, может быть, так:

(defn replace-characters [s rep]
  (apply str (map #(rep (str %) %) s)))

user> (replace-characters "is it possible? sure!" {"!" "[exclamation mark]"
                                                            "?" "[question mark]"})

;;=> "is it possible[question mark] sure[exclamation mark]"

string/escape делает именно то, что вы хотите:

(string/escape "Hi!" {\! "Exclamation Mark"})
;; => "HiExclamation Mark"

эта функция заменяет символы, используя карту от символа к замене.

Если вы хотите заменить регулярные выражения или строку, вы можете использовать reduce-kv в сочетании с string/replace:

(def replacements
  (array-map
   "!" "Exclamation Mark"
   #"[:/?#\[\]@!$&'()*+,;=]" ""
   #_more_replacements))

(defn replace [s replacements]
  (reduce-kv string/replace s replacements))

Таким образом, вы выполняете цикл (по порядку, когда replacements является array-map) по ключу и значениям замен и применяете их к строке s, используя string/replace.

(replace "Hi!" replacements)
;; => "HiExclamation Mark"

если порядок элементов в array-map обратный, замена ! с "" (поскольку ! находится в регулярном выражении) будет успешным:

(replace "Hi!" (into (array-map) (reverse replacements)))
;; => "Hi"

Это решение будет работать, но мне нужно, чтобы ключи на карте были строкой, clojure.string/escape по моему опыту не работает для строк в качестве ключей.

Rawley Fowler 17.05.2022 16:50

Другие вопросы по теме