Новичок в clojure: токенизация списков разных персонажей

Так что я знаю, что это не лучший метод решения этой проблемы, но я пытаюсь просмотреть список строк из входного файла, который в конечном итоге является выражением. У меня есть список выражений, и у каждого выражения есть свой собственный список благодаря функции разделения списка. Мой следующий шаг - заменить персонажей id, целые с intи + или - с addop, У меня есть регулярные выражения, чтобы определить, соответствуют ли мои символы каким-либо из них, но когда я пытаюсь заменить их, я могу получить только последний цикл for, который я вызываю, чтобы оставить какие-либо длительные результаты. Я знаю, к чему это сводится, как работает функциональное программирование, но я не могу обернуть голову следом этой программы, и как заменить каждый отдельный тип ввода и сохранить результаты все в одном списке.

(def reint #"\d++")
(def reid #"[a-zA-Z]+")
(def readdop #"\+|\-")

(def lines (into () (into () (clojure.string/split-lines (slurp "input.txt")) )))

(defn split-the-line [line] (clojure.string/split line #" " ))

(defn split-the-list  [] (for [x (into [] lines)] (split-the-line x)))

(defn tokenize-the-line [line] 
(for [x line] (clojure.string/replace x reid "id")) 
(for [x line] (clojure.string/replace x reint "int")) 
(for [x line] (clojure.string/replace x readdop "addop")))

(defn tokenize-the-list [] (for [x (into [] (split-the-list) )] (tokenize-the-line x)))

И, как вы, вероятно, можете сказать, я довольно новичок в функциональном программировании, поэтому любые советы приветствуются!

1 ответ

Решение

Вы используете do блок, который оценивает несколько выражений (обычно для побочных эффектов), а затем возвращает последнее. Вы не можете видеть это, потому что fn (и поэтому defn) неявно содержат один. Как таковые, линии

(for [x line] (clojure.string/replace x reid "id")) 
(for [x line] (clojure.string/replace x reint "int")) 

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

(for [x line] 
     (-> x 
         (clojure.string/replace reid "id")
         (clojure.string/replace reint "int")
         (clojure.string/replace readdop "addop")))

Это создает код, который делает reid заменить x в качестве первого аргумента, то делает reint замените его результатом в качестве первого аргумента и так далее.

В качестве альтернативы вы можете сделать это с помощью comp составлять анонимные функции, такие как (fn [s] (clojure.string/replace s reid "id") (частичное применение replace). В мире императивов мы довольно привыкли к запуску нескольких процедур, которые "удаляют данные на месте" - в функциональном мире вы чаще объединяете несколько функций для выполнения всех операций, а затем запускаете результат.

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