Clojure:如何在exception时重现?
我试图在放弃例外之前执行一次func。 但是在Clojure中从catch block重复出现是无效的。 这怎么能实现?
(loop [tries 10] (try (might-throw-exception) (catch Exception e (when (pos? tries) (recur (dec tries)))))) java.lang.UnsupportedOperationException: Cannot recur from catch/finally
我能find的最好的是以下笨拙的解决scheme(包装在func中并调用它)
(defn do-it [] (try (might-throw-exception) (catch Exception e nil))) (loop [times 10] (when (and (nil? (do-it)) (pos? times)) (recur (dec times))))
macros正在调用…
这个怎么样:
(defn try-times* "Executes thunk. If an exception is thrown, will retry. At most n retries are done. If still some exception is thrown it is bubbled upwards in the call chain." [n thunk] (loop [nn] (if-let [result (try [(thunk)] (catch Exception e (when (zero? n) (throw e))))] (result 0) (recur (dec n))))) (defmacro try-times "Executes body. If an exception is thrown, will retry. At most n retries are done. If still some exception is thrown it is bubbled upwards in the call chain." [n & body] `(try-times* ~n (fn [] ~@body)))
kotarak的想法是要走的路,但是这个问题让我感到痒痒,所以我想提供一个我喜欢的同一主题的即兴创作,因为它不使用循环/重复:
(defn try-times* [thunk times] (let [res (first (drop-while #{::fail} (repeatedly times #(try (thunk) (catch Throwable _ ::fail)))))] (when-not (= ::fail res) res)))
将try-timesmacros保持原样。
如果你想让thunk返回nil,你可以放下let / when对,让:: fail表示“n次函数失败”,而nil表示“函数返回nil”。 这种行为会更灵活,但不太方便(调用者必须检查::看看它是否工作,而不是零),所以也许最好实现为一个可选的第二个参数:
(defn try-times* [thunk n & fail-value] (first (drop-while #{fail-value} ...)))
我的build议是:
(defmacro try-times "Retries expr for times times, then throws exception or returns evaluated value of expr" [times & expr] `(loop [err# (dec ~times)] (let [[result# no-retry#] (try [(do ~@expr) true] (catch Exception e# (when (zero? err#) (throw e#)) [nil false]))] (if no-retry# result# (recur (dec err#))))))
一次将打印“没有错误”:
(try-times 3 (println "no errors here") 42)
将打印“试”3次,然后扔零分:
(try-times 3 (println "trying") (/ 1 0))
一个try-times
macros是优雅的,但是对于一次性的,只要从try
块中取出即可:
(loop [tries 10] (when (try (might-throw-exception) false ; so 'when' is false, whatever 'might-throw-exception' returned (catch Exception e (pos? tries))) (recur (dec tries))))
还有一个解决scheme,没有macros
(defn retry [& {:keys [fun waits ex-handler] :or {ex-handler #(log/error (.getMessage %))}}] (fn [ctx] (loop [[time & rem] waits] (let [{:keys [res ex]} (try {:res (fun ctx)} (catch Exception e (when ex-handler (ex-handler e)) {:ex e}))] (if-not ex res (do (Thread/sleep time) (if (seq rem) (recur rem) (throw ex))))))))