Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/sql-server/26.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
If statement 在Clojure函数中写入多个if检查的更好方法?_If Statement_Clojure_Idioms - Fatal编程技术网

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

If statement 在Clojure函数中写入多个if检查的更好方法?,if-statement,clojure,idioms,If Statement,Clojure,Idioms,我有一个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-us

我有一个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)))))))
这本书写得不错,但有点难读,所以我想知道是否有更地道的Clojure写作方式

我考虑过做一些
简单的验证
使用
计算东西,以及
使用
抛出异常和使用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

谢谢

如果验证失败表明存在错误情况,则异常(和try-catch块)可能是处理它的最佳方式。特别是如果不是“正常”情况(即无效的客户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))
话虽如此,您现在使用
nil
表示“数据验证失败”,因此数据中不能包含
nil


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

我调用了
,默认情况除外

(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
(defn proc num[n]
(如果不是(编号?n)
(抛出(IllegalArgumentException.“不是数字”))
n)
(defn proc int[n]
(如果不是(int?n)
(抛出(IllegalArgumentException.“Not int”))
n)
(defn proc奇数[n]
(如果不是(奇数?n)
(抛出(IllegalArgumentException.“非奇数”))
n)
(定义过程十[n]
(如果不是(<10 n)
(抛出(IllegalArgumentException.“不够大”))
n)
(defn进程[arg]
(默认值为42;arg除外)
进程数
进程整型
进程奇数
过程(十)
(过程零)=>42
(过程:a)=>42
(处理“foo”)=>42
(过程12)=>42
(过程13)=>13

这避免了对
nil
或任何其他“sentinal”值赋予特殊含义,并使用
异常
用于在出现错误时更改控制流的正常目的。

这是Clojure代码库中的常见问题。一种方法是将数据包装成提供更多信息的内容,即操作是否成功。有几个库可以帮助您实现这一点

例如猫():

或者有结果-我在这一点上提供了帮助():


我也面临同样的问题。我的解决方案是复制some->>宏并稍微调整它:

(defmacro run-until->> [stop? expr & forms]
     (let [g (gensym)
           steps (map (fn [step] `(if (~stop? ~g) ~g (->> ~g ~step)))
               forms)]
        `(let [~g ~expr
               ~@(interleave (repeat g) (butlast steps))]
             ~(if (empty? steps)
                g
                (last steps)))))
此宏将检查预定义的条件,而不是检查nils。例如:

(defn validate-data [[status data]]
    (if (< (:a data) 10)
       [:validated data]
       [:failed data]))

(defn calculate-1 [[status data]]
     [:calculate-1 (assoc data :b 2)])

(defn calculate-2 [[status data]]
    (if (:b data)
       [:calculate-2 (update data :b inc)]
       [:failed data]))

(deftest test
    (let [initial-data [:init {:a 1}]]
       (is (= [:calculate-2 {:a 1, :b 3}] 
              (run-until->> #(= :failed (first %))
                            initial-data
                            (validate-data)
                            (calculate-1)
                            (calculate-2))))

       (is (= [:failed {:a 1}] 
              (run-until->> #(= :failed (first %))
                            initial-data
                            (validate-data)
                            (calculate-2))))))
(defn验证数据[[状态数据]]
(如果(<(:a数据)10)
[:验证数据]
[:失败的数据])
(定义计算-1[[状态数据]]
[:计算-1(关联数据:B2)])
(定义计算-2[[状态数据]]
(如果(:b数据)
[:calculate-2(更新数据:b inc)]
[:失败的数据])
(除雾试验
(让[初始数据[:init{:a1}]]
(是(=[:calculate-2{:a1,:b3}]
(运行到->>#(=:失败(第一个%)
初始数据
(验证数据)
(计算-1)
(计算-2)
(是(=[:失败的{:a 1}]
(运行到->>#(=:失败(第一个%)
初始数据
(验证数据)
(计算-2(()()))

其他答案中的一个示例使用了有缺陷的
某些->
宏:每次失败都应将消息打印到控制台并返回
nil
。这是不好的,因为
nil
值也可能指示良好的结果,尤其是对于空集合。不用说,您不仅需要打印错误,还需要以某种方式处理错误或将其记录在某个地方

重构代码最简单的方法就是分解代码。比方说,您可以将第一个
if
的负分支的所有内容放在一个单独的函数中,就是这样。这两个函数将更易于测试和调试

对我来说,这将是最好的选择,因为它将立即解决问题

有例外的情况也是好的。不要发明自己的异常类,只需使用
exinfo
抛出一个映射即可。一旦捕获,此类异常将返回与其一起抛出的所有数据:

(if (some-checks data)
  (some-positive-code data)
  (throw (ex-into "Some useful message" {:type :error 
                                         :data data})))
要抓住它:

(try
  (some-validation data)
(catch Exception e
  (let [err-data (ex-data e)]
    ; ...)))

最后,可能有使用单子的情况,但要注意过度设计问题。

我创建单子就是为了处理这种情况。

这是我在许多情况下开始做的。它最终的效果很像哈斯克尔的链锁。一旦它在链的某个地方失败,整个过程就会失败并返回零。最后我使用了异常,感谢您的建议奇妙的答案-清晰而直接。谢谢你!另见。
(defmacro run-until->> [stop? expr & forms]
     (let [g (gensym)
           steps (map (fn [step] `(if (~stop? ~g) ~g (->> ~g ~step)))
               forms)]
        `(let [~g ~expr
               ~@(interleave (repeat g) (butlast steps))]
             ~(if (empty? steps)
                g
                (last steps)))))
(defn validate-data [[status data]]
    (if (< (:a data) 10)
       [:validated data]
       [:failed data]))

(defn calculate-1 [[status data]]
     [:calculate-1 (assoc data :b 2)])

(defn calculate-2 [[status data]]
    (if (:b data)
       [:calculate-2 (update data :b inc)]
       [:failed data]))

(deftest test
    (let [initial-data [:init {:a 1}]]
       (is (= [:calculate-2 {:a 1, :b 3}] 
              (run-until->> #(= :failed (first %))
                            initial-data
                            (validate-data)
                            (calculate-1)
                            (calculate-2))))

       (is (= [:failed {:a 1}] 
              (run-until->> #(= :failed (first %))
                            initial-data
                            (validate-data)
                            (calculate-2))))))
(if (some-checks data)
  (some-positive-code data)
  (throw (ex-into "Some useful message" {:type :error 
                                         :data data})))
(try
  (some-validation data)
(catch Exception e
  (let [err-data (ex-data e)]
    ; ...)))