Я должен использовать функцию или макрос для проверки аргументов в Clojure?

Будет ли работать мультиселектор для того, что вы делаете? Api Doc здесь.

12
задан clartaq 28 October 2009 в 21:42
поделиться

3 ответа

Clojure уже есть (недокументировано, возможно, будет изменено) поддержка предварительных и пост-условий на fn s.

user> (defn divide [x y]
        {:pre [(not= y 0)]}
        (/ x y))
user> (divide 1 0)
Assert failed: (not= y 0)
   [Thrown class java.lang.Exception]

Хотя вид уродливый.

Я бы, вероятно, написал макрос только для того, чтобы кратко сообщить, какие тесты завершились неудачно (цитировать и распечатывать тест буквально). Код CL, с которым вы связались, выглядит довольно неприятно с этим огромным оператором case. На мой взгляд, здесь лучше использовать мультиметоды. Вы можете сами довольно легко собрать что-то подобное.

(defmacro assert* [val test]
  `(let [result# ~test]              ;; SO`s syntax-highlighting is terrible
     (when (not result#)
       (throw (Exception.
               (str "Test failed: " (quote ~test)
                    " for " (quote ~val) " = " ~val))))))

(defmulti validate* (fn [val test] test))

(defmethod validate* :non-zero [x _]
  (assert* x (not= x 0)))

(defmethod validate* :even [x _]
  (assert* x (even? x)))

(defn validate [& tests]
  (doseq [test tests] (apply validate* test)))

(defn divide [x y]
  (validate [y :non-zero] [x :even])
  (/ x y))

Затем:

user> (divide 1 0)
; Evaluation aborted.
; Test failed: (not= x 0) for x = 0
;   [Thrown class java.lang.Exception]

user> (divide 5 1)
; Evaluation aborted.
; Test failed: (even? x) for x = 5
;   [Thrown class java.lang.Exception]

user> (divide 6 2)
3
13
ответ дан 2 December 2019 в 19:54
поделиться

Несколько мыслей.

У меня такое ощущение, что это зависит от сложности и количества проверок, а также от характера функций.

Если вы выполняете очень сложные проверки, вы должны вывести валидаторы из функций. Причина в том, что вы можете использовать более простые для создания более сложных.

Например, вы пишете:

  1. валидатор, чтобы убедиться, что список не пуст,
  2. валидатор, чтобы убедиться, что значение больше нуля,
  3. используйте 1 и 2, чтобы убедиться, что значение не является пустым списком значений больше нуля.

Если вы просто выполняете огромное количество простых проверок, и ваша проблема - многословие, ( например, у вас есть 50 функций, для которых требуются ненулевые целые числа), тогда макрос, вероятно, имеет больше смысла.

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

Наконец, обратиться к «другим, о которых вы не думали». Clojure поддерживает общую диспетчерскую p на основе функции диспетчеризации. Эта функция может отправлять соответствующий код или сообщения об ошибках на основе любого количества факторов.

3
ответ дан 2 December 2019 в 19:54
поделиться

Случай, когда вам нужен макрос , может быть, если вы хотите изменить язык для автоматического добавления тестов к любой функции, определенной в блоке, например:

(with-function-validators [test1 test2 test4]  
    (defn fun1 [arg1 arg2] 
        (do-stuff))
    (defn fun2 [arg1 arg2] 
        (do-stuff))
    (defn fun3 [arg1 arg2] 
        (do-stuff)))  
1
ответ дан 2 December 2019 в 19:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: