2010-08-24 1 views
12

Je suis toujours en train d'essayer d'envelopper Clojure. Je peux voir comment implémenter ce qui suit dans Haskell, Python, etc. mais je ne comprends pas encore comment écrire ceci dans Clojure. Appréciez si quelqu'un peut me montrer la structure de base. Pseudo-code ci-dessous.Basic Clojure: Comment faire une série de si-alors?

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 

Serait-ce un groupe de locations, puis une expression finale? Merci.

+0

est-ce que la valeur "bad_value" est identique pour les trois cas? –

Répondre

20

Il y a un certain nombre de possibilités - voici quelques pour commencer:

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

Excellente réponse, encore une fois. +1 Ma réponse est supprimée. – Isaac

+1

@Isaac Hodes: Merci! En passant, je me demande si écrire une * fonction * à la place d'une macro est une option si on ne veut pas coder un seul type d'exception pour toutes les invocations ... Utiliser 'proxy' pour les classes d'exceptions est idiot, mais peut-être condition/ccerror-kit pourrait aider? J'ai vraiment besoin d'enquêter correctement sur ces choses dans un avenir proche. –

+0

Je n'ai pas regardé l'un ou l'autre - ils ont l'air intéressants, et assez capables ... pourraient avoir besoin de les explorer. Merci pour les pointeurs! – Isaac

4

Une version sans macros, non testé, mais je pense que cela fonctionnerait:

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

J'Enveloppez la exceptions dans les fonctions de sorte qu'ils ne sont pas générés, sauf si vous avez besoin de les jeter. La version de Michał avec des macros est plus propre.

6

vous pouvez utiliser cond:

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

l'autre est facultative;)

PS: Je ne sais pas comment lancer une exception dans Clojure alors ne me dites pas que le code ne fonctionne pas travail

0

Vous devriez lancer des exceptions dans vos fonctions geta,, getc. Ensuite, votre code est juste:

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

' (fait quelque chose abc) 'retourne' c'. -avec abc) '? – Thumbnail