I don't know how to implement this piece of Python code in Clojure
for i in range(3):
try:
......
except e:
if i == 2:
raise e
else:
continue
else:
break
I wonder why something so simple in Python is so hard in Clojure. I think the difficulty is because Clojure is a functional programming language and thus is not suitable for such an imperative task. This is my attempt:
(first
(remove #(instance? Exception %)
(for [i (range 3)]
(try (......)
(catch Exception e
(if (== i 2)
(throw e)
e)))))))
It is very ugly, and worse, it doesn't work as expected. The for loop is actually evaluated fully instead of lazily (I realized this when I put a println inside).
If anyone has a better idea to implement that, please enlighten me.
Similar to Marcyk's answer, but no macro trickery:
(defn retry
[retries f & args]
(let [res (try {:value (apply f args)}
(catch Exception e
(if (zero? retries)
(throw e)
{:exception e})))]
(if (:exception res)
(recur (dec retries) f args)
(:value res))))
Slightly complicated because you can't recur
inside a catch
clause. Note that this takes a function:
(retry 3 (fn []
(println "foo")
(if (zero? (rand-int 2))
(throw (Exception. "foo"))
2)))
=>
foo ;; one or two or three of these
foo
2
Here's one approach:
(defmacro retry
"Evaluates expr up to cnt + 1 times, retrying if an exception
is thrown. If an exception is thrown on the final attempt, it
is allowed to bubble up."
[cnt expr]
(letfn [(go [cnt]
(if (zero? cnt)
expr
`(try ~expr
(catch Exception e#
(retry ~(dec cnt) ~expr)))))]
(go cnt)))
Example from the REPL:
user> (retry 2 (do (println :foo) (throw (RuntimeException. "foo"))))
:foo
:foo
:foo
; Evaluation aborted.
(Passing 2
to retry
asks expr
to be retried twice it if fails the first time round, for a total of three attempts. Three :foo
s are printed, because the println
occurs before the throw
in the do
form passed to retry
. The final ; Evaluation aborted.
means an exception was thrown.)
Also, about the for
loop from your snippet:
If you try looping over a longer range (replace (range 3)
with (range 10)
, say), the output will end after i
reaches 3
. Also, if you put in a println
before the form which throws the exception, it will of course print out whatever you pass to it; if the println
occurs after the exception-throwing form, there will be no printout. In any case, at most three calls to println
will be executed (assuming an exception is thrown on every iteration).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With