在抛出异常之前重试了3次 - 在clojure中

use*_*023 5 clojure

我不知道如何在Clojure中实现这段Python代码

for i in range(3):
    try:
        ......
    except e:
        if i == 2:
            raise e
        else:
            continue
    else:
        break
Run Code Online (Sandbox Code Playgroud)

我想知道为什么Python中这么简单的东西在Clojure中如此之难.我认为困难在于因为Clojure是一种函数式编程语言,因此不适合这样的命令式任务.这是我的尝试:

(first
  (remove #(instance? Exception %)
    (for [i (range 3)]
      (try (......)
              (catch Exception e
                (if (== i 2) 
                  (throw e)
                  e)))))))
Run Code Online (Sandbox Code Playgroud)

它非常丑陋,更糟糕的是,它没有按预期工作.for循环实际上是完全评估而不是懒惰(我在内置println时意识到这一点).

如果有人有更好的想法来实现它,请赐教.

Joo*_*aat 12

类似于Marcyk的答案,但没有宏观诡计:

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

稍微复杂,因为你不能recur在一个catch条款内.请注意,这需要一个功能:

(retry 3 (fn [] 
          (println "foo") 
          (if (zero? (rand-int 2))
              (throw (Exception. "foo"))
              2)))
=>
foo ;; one or two or three of these
foo
2
Run Code Online (Sandbox Code Playgroud)


Mic*_*zyk 7

这是一种方法:

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

REPL的示例:

user> (retry 2 (do (println :foo) (throw (RuntimeException. "foo"))))
:foo
:foo
:foo
; Evaluation aborted.
Run Code Online (Sandbox Code Playgroud)

(传递2retry询问expr重试两次,如果轮失败第一次,总共三次尝试的.三个:foos的打印,因为println在之前发生throwdo传递给形式retry.最终的; Evaluation aborted.装置抛出异常.)

另外,关于for你的代码片段的循环:

如果您尝试遍历更远的射程(替换(range 3)(range 10),说),输出将结束之后i到达3.此外,如果您在println抛出异常的表单之前放入一个,它当然会打印出您传递给它的任何内容; 如果在println抛出异常形式之后发生,则不会打印输出.在任何情况下,最多println将执行三次调用(假设每次迭代都抛出异常).