在Clojure函数中检查多个if的更好方法?

Gen*_*Hal 3 if-statement idiomatic clojure

我有一个Clojure函数,看起来像下面这样.

(defn calculate-stuff [data]
  (if (some-simple-validation data)
    (create-error data)
    (let [foo (calculate-stuff-using data)]
      (if (failed? foo)
        (create-error foo)
        (let [bar (calculate-more-stuff-using foo)]
          (if (failed? bar)
            (create-error bar)
            (calculate-response bar)))))))
Run Code Online (Sandbox Code Playgroud)

哪个工作正常,但有点难以阅读,所以我想知道是否有更惯用的Clojure写作方式?

我考虑过制作some-simple-validation,calculate-stuff-usingcalculate-more-stuff-using抛出异常并使用try/catch块,但感觉就像使用控制流的异常一样,感觉不正确.

我不能让异常逃避这个功能,因为我正在使用它来映射一系列地图,我仍然想继续处理剩余部分.

我猜我所追求的是这样的?

(defn calculate-stuff [data]
  (let-with-checking-function
    [valid-data (some-simple-validation data)
     foo (calculate-stuff-using valid-data)
     bar (calculate-more-stuff-using foo)]
    failed?)                    ; this function is used to check each variable
      (create-error %)          ; % is the variable that failed
      (calculate-response bar)) ; all variables are OK
Run Code Online (Sandbox Code Playgroud)

谢谢!

Ala*_*son 6

如果验证失败指示错误情况,则异常(和try-catch块)可能是处理错误的最佳方式.特别是如果它不是"正常"的出现(即无效的cust-id等).

对于更"正常"但仍然"无效"的情况,您可以使用some->(发音为"some-thread")来静静地压制"坏"情况.只需让验证器返回nil错误数据,并some->中止处理链:

(defn proc-num [n]
  (when (number? n)
    (println :proc-num n)
    n))

(defn proc-int [n]
  (when (int? n)
    (println :proc-int n)
    n))

(defn proc-odd [n]
  (when (odd? n)
    (println :proc-odd n)
    n))

(defn proc-ten [n]
  (when (< 10 n)
    (println :proc-10 n)
    n))

(defn process [arg]
  (when (nil? arg)
    (throw (ex-info "Cannot have nil data" {:arg arg})))
  (some-> arg
    proc-num
    proc-int
    proc-odd
    proc-ten))
Run Code Online (Sandbox Code Playgroud)

结果:

(process :a) => nil

(process "foo") => nil

:proc-num 12
:proc-int 12
(process 12) => nil

:proc-num 13
:proc-int 13
:proc-odd 13
:proc-10 13
(process 13) => 13

(throws? (process nil)) => true
Run Code Online (Sandbox Code Playgroud)

说完这个,你现在nil用来表示"数据验证失败",所以你不能拥有nil数据.


使用无效数据的例外

使用nil短路处理的特殊值可以起作用,但使用普通的异常可能更容易,特别是对于明显是"坏数据"的情况:

(defn parse-with-default [str-val default-val]
  (try
    (Long/parseLong str-val)
    (catch Exception e
      default-val))) ; default value

(parse-with-default "66-Six" 42) => 42
Run Code Online (Sandbox Code Playgroud)

我有一个小宏来自动化这个过程,称为with-exception-default:

(defn proc-num [n]
  (when-not (number? n)
    (throw (IllegalArgumentException. "Not a number")))
  n)

(defn proc-int [n]
  (when-not (int? n)
    (throw (IllegalArgumentException. "Not int")))
  n)

(defn proc-odd [n]
  (when-not (odd? n)
    (throw (IllegalArgumentException. "Not odd")))
  n)

(defn proc-ten [n]
  (when-not (< 10 n)
    (throw (IllegalArgumentException. "Not big enough")))
  n)

(defn process [arg]
  (with-exception-default 42  ; <= default value to return if anything fails
    (-> arg
      proc-num
      proc-int
      proc-odd
      proc-ten)))

(process nil)    => 42
(process :a)     => 42
(process "foo")  => 42
(process 12)     => 42

(process 13)     => 13
Run Code Online (Sandbox Code Playgroud)

这避免给予特殊含义nil或任何其他"sentinal"值,并且Exception用于在出现错误时改变控制流的正常目的.