Подтвердить что ты не робот

Basic Clojure: Как сделать серию if-then?

По-прежнему пытаюсь обернуть голову вокруг Clojure. Я вижу, как реализовать следующее в Haskell, Python и т.д., Но пока не понимаю, как записать это в Clojure. Цените, если кто-то может показать мне основную структуру. Псевдокод ниже.

a = get_a
if (a == bad_value) then throw exception_a
b = get_b
if (b == bad_value) then throw exception_b
c = get_c
if (c == bad_value) then throw exception_c
...
do_action_with a b c

Будет ли это связкой let, а затем окончательным выражением? Спасибо.

4b9b3361

Ответ 1

Существует несколько возможностей - здесь несколько для запуска:

;;; 1. direct translation
; _ is the idiomatic "I don't care" identifier in Clojure
(let [a (get-a)
      _ (if (= a bad-value) (throw (Exception. "Foo!")))
      b (get-b)
      _ (if (= b bad-value) (throw (Exception. "Foo!")))
      ...]
  (do-action-with a b ...))

;;; 2. abstract the pattern away
(defmacro disallow
  ([expr val] ; binary version with default exception type;
              ; omit if explicit type is to be required
     (disallow (list* &form [Exception]) &env expr val Exception))
  ([expr val e]
     `(let [actual# ~expr]
        (if (= actual# ~val)
          (throw (new ~e (str "Value " ~val " not allowed.")))
          actual#))))

(let [a (disallow (get-a) ExceptionA)
      b (disallow (get-b) ExceptionB)
      ...]
  ...)

;;; 3. monadic short-circuiting
(use '[clojure.contrib.monads :only [domonad maybe-m]])
; ...now do it more or less as in Haskell;
; you can use :when in domonad for monads with m-zero
; -- the syntax is that of for / doseq:
(doseq [sym '[a b c]] (intern *ns* sym (atom 0)))
(domonad maybe-m
  [a @a
   :when (pos? a)
   b @b
   :when (neg? b)
   c @c
   :when (not (zero? c))]
  (* a b c))
; => 0
(dorun (map reset! [a b c] [3 -2 1]))
(domonad maybe-m
  ; same as above
  )
; => -6

Ответ 2

вы можете использовать cond:

(cond
  (= a bad _value) exception_a
  (= b bad _value) exception_b
  (= c bad _value) exception_c
  :else default)

else необязательно;)

PS: Я не знаю, как сделать исключение в Clojure, так что не говорите мне, что код не работает

Ответ 3

Версия без макросов, непроверенная, но я думаю, что это сработало бы:

(defn check [bad xs]
  (for [[f e] (partition 2 xs) :let [x (f)]]
    (if (= x bad)
      (throw (e)) 
      x)))

(let [[a b c] (check bad [get-a #(ExceptionA.)
                          get-b #(ExceptionB.)
                          get-c #(ExceptionC.)])]
  (do-action-with a b c))

Я завернул исключения в функциях, чтобы они не были сгенерированы, если вам не нужно их бросать. Версия Michał с макросами более чистая.

Ответ 4

Вы должны исключить исключения из своих функций geta, getb, getc. Тогда ваш код просто:

(let [ a (geta)
       b (getb)
       C (getc)]
   ( do something a b c))