Clojure:如何重复异常?

Gab*_*iMe 40 clojure

我试图在放弃异常之前多次执行一个函数.但是在Clojure中从catch块中重现是无效的.怎么能实现这一目标?

(loop [tries 10]
  (try
    (might-throw-exception)
    (catch Exception e
      (when (pos? tries) (recur (dec tries))))))

java.lang.UnsupportedOperationException: Cannot recur from catch/finally 
Run Code Online (Sandbox Code Playgroud)

我能找到的最好的是以下笨拙的解决方案(包装在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))))
Run Code Online (Sandbox Code Playgroud)

kot*_*rak 44

宏正在呼唤......

这个怎么样:

(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 [n n]
    (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)))
Run Code Online (Sandbox Code Playgroud)

  • 我可以问你为什么把thunk的结果放在一个向量中?我不确定为什么你不能把它作为一个"裸"值? (4认同)
  • @ChristopheDeTroyer否则如果(thunk)返回nil,它将被视为if-let的错误结果 (4认同)

ama*_*loy 12

kotarak的想法是要走的路,但这个问题让我感到奇怪,所以我想提供一个与我喜欢的相同主题的riff,因为它不使用loop/recur:

(defn try-times* [thunk times]
  (let [res (first (drop-while #{::fail}
                               (repeatedly times
                                           #(try (thunk)
                                                 (catch Throwable _ ::fail)))))]
    (when-not (= ::fail res)
      res)))
Run Code Online (Sandbox Code Playgroud)

并保留try-times宏.

如果你想允许thunk返回nil,你可以删除let/when对,let :: fail表示"函数失败n次",而nil表示"函数返回nil".这种行为会更灵活但不太方便(调用者必须检查:: fail以查看它是否有效而不仅仅是nil),所以最好将它作为可选的第二个参数实现:

(defn try-times* [thunk n & fail-value]
  (first (drop-while #{fail-value} ...)))
Run Code Online (Sandbox Code Playgroud)


Eug*_*sky 7

一个try-times宏是优雅的,但对于一个一次性的,只是拉你when的出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))))
Run Code Online (Sandbox Code Playgroud)