2010-08-24 8 views
12

Todavía estoy tratando de abarcar Clojure. Puedo ver cómo implementar lo siguiente en Haskell, Python, etc. pero aún no entiendo cómo escribir esto en Clojure. Apreciar si alguien puede mostrarme la estructura básica. Pseudo-código a continuación.Clojure Básico: ¿Cómo hacer una serie de si, entonces?

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 

¿Sería un montón de deja y luego una expresión final? Gracias.

+0

es 'bad_value' igual para los tres casos? –

Respuesta

20

Hay una serie de posibilidades - estos son algunos aspectos para un comienzo:

;;; 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 
+1

Excelente respuesta, nuevamente. +1. Mi respuesta es eliminada. – Isaac

+1

@Isaac Hodes: ¡Gracias! Por cierto, me pregunto si escribir una * función * en lugar de una macro es una opción si no estamos dispuestos a codificar un único tipo de excepción para todas las invocaciones ... Usar 'proxy' para las clases de excepción parece una tontería, pero tal vez cc condición/ccerror-kit podría ayudar? Realmente necesito investigar esos adecuadamente en algún momento pronto. –

+0

No he mirado ninguno de esos: se ven interesantes y bastante capaces ... podría necesitar explorarlos. Gracias por los consejos! – Isaac

4

Una versión sin macros, no probado, pero creo que esto funcionaría:

(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)) 

que envuelve la excepciones en funciones para que no se generen a menos que sea necesario tirarlas. La versión de Michał con macros es más limpia.

6

puede utilizar cond:

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

la otra es opcional;) ​​

PD: No sé cómo lanzar una excepción en Clojure por lo que no me digan que el código no hace trabajo

0

Debe lanzar e excepciones en sus funciones geta, getb, getc. Entonces su código es simplemente:

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

' (haga algo abc) 'devuelve' c'. Tal vez '(do-something) -with abc) '? – Thumbnail

Cuestiones relacionadas