Новичок в 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
). В мире императивов мы довольно привыкли к запуску нескольких процедур, которые "удаляют данные на месте" - в функциональном мире вы чаще объединяете несколько функций для выполнения всех операций, а затем запускаете результат.