Я думаю, что основной ответ на ваш вопрос заключается в том, что функциональное программирование больше связано с вводом и выводом (подумайте о математической функции), тогда как императивное программирование, как правило, больше связано с побочными эффектами и изменяемыми данными. Вместо того, чтобы думать: «Что мне нужно делать?», подумайте: «Какая структура данных является моей целью и как мне определить ее?» У вас также могут быть побочные эффекты (например, печать), но обычно вы пишете чистые функции, которые принимают аргументы и что-то возвращают.
Деструктуризация — бесценный инструмент в Clojure, и здесь он может пригодиться. . Вы хотите разбить строку на две строки с помощью clojure.string/split
, а затем сделать что-то с одной из строк и что-то еще с другой. Вы можете использовать привязку let
для присвоения имен каждой строке, например:
(let [[str1 str2] (clojure.string/split signed-request #"\.")]
(do-stuff-with-str1-and-str2))
Я не слишком хорошо знаком с этой конкретной проблемой, но, основываясь на трех шагах, которые вы перечислили, похоже, вы получите 2 результата, по одному из каждой строки. Итак, возможно, вам следует сосредоточиться на написании функции, которая возвращает вектор, содержащий 2 результата, например:
(defn process-signed-request [signed-request]
(let [[str1 str2] (clojure.string/split signed-request #"\.")]
[(compare-fn (decode-with-base-64 str1) secret)
(decode-with-json (decode-with-base-64 str2))]))
Обратите внимание, что приведенное выше частично является псевдокодом — вам нужно будет заменить compare-fn
, decode-with-base-64
, decode-with-json
и secret
фактическим кодом, представляющим эти вещи — это, или вы можете оставить его как есть и просто реализовать функции так, чтобы compare-fn
, decode-with-base-64
и decode-with-json
относятся к реальным функциям, которые вы пишете. Это распространенный подход в функциональном программировании — напишите короткую высокоуровневую функцию, определяющую решение проблемы, а затем вернитесь и напишите вспомогательные функции, которые она использует.
Кстати, есть еще несколько способов написать часть (decode-with-json (decode-with-base-64 str2))
:
((comp decode-with-json decode-with-base-64) str2)
(-> str2 decode-with-base-64 decode-with-json)
Я часто нахожу второй подход с использованием макроса потоковой передачи (->
или ->>
) полезным, когда я знаю последовательность действий, которые мне нужно сделать с объектом, и я хочу, чтобы код читался интуитивно. Мне легче читать, например, «взять str2
, декодировать его с помощью базы 64, а затем снова декодировать с помощью JSON».
Кроме того, это всего лишь мелочь, но обратите внимание на порядок скобок, когда пишете код на Clojure. В том виде, в котором у вас сейчас есть код, круглые скобки должны выглядеть так:
(defn parse-request [signed_request]
(clojure.string/split signed_request #"\."))
(defn redirect-page [signed_request]
(layout/render "redirect.html"
{:parsed_request (parse-request signed_request)}))
Если у вас большой опыт работы с императивами, вы, вероятно, настолько укоренились в синтаксисе fn(x)
, что случайно вводите его вместо синтаксиса (fn x)
Lisp, который использует Clojure.
И пока я придираюсь, в Clojure идиоматично использовать дефисы вместо подчеркивания для именования символов. Итак, я бы переименовал signed_request
и :parsed_request
в signed-request
и :parsed-request
.
Надеюсь, это поможет!
14.04.2014