开发者

Consolidated cond arguments in Clojure (CL style)

开发者 https://www.devze.com 2023-01-24 06:11 出处:网络
In Cloju开发者_如何学Gore I do this (println (cond false \"don\'t care\" \"otherwise\" \"otherwise\"))

In Cloju开发者_如何学Gore I do this

(println (cond false "don't care" "otherwise" "otherwise"))

In Common LISP this would be

(print (cond (nil "don't care") ("otherwise") ))

Is there a way to get this kind of simplified cond in Clojure?


Version which includes a fix for that Alex Taggart noticed below. Passes all the test cases shown in the test. It allows for arbitrary clauses passed to my-cond to be length 1 instead of 2 which results in the length 1 clause being both the test for truthiness and the result if it is true. Based off my limited experience with CL I actually think this behavior is different from what cond does but seems to be in line with how I've interpreted what you're asking for. Kotarak's answer seems to line up with the CL one as using the last statement in the CL cond seems to match up with using the :else clause in the Clojure version.

Regardless here is a solution where it should allow any clause to be a length of one and use that for both the truth test and result.

(defmacro my-cond
  [& others]
  (if others
    (let [more# (next others)
          extra-clauses# (if more# `(my-cond ~@more#))
          clause# (first others)]
      (if (= 2 (count clause#))
        `(if ~(first clause#) ~(second clause#) ~extra-clauses#)
        `(if ~(first clause#) ~(first clause#)  ~extra-clauses#)))))


(deftest my-cond-works
  (is (= 3 (my-cond (false 1) (false 2) (3))))
  (is (= "otherwise" (my-cond (false "don't care") ("otherwise"))))
  (is (= nil (my-cond (:one nil) ("otherwise"))))
  (is (= "care" (my-cond (1 "care") ("otherwise"))))
  (is (= "otherwise" (my-cond (false "care") ("otherwise") (true "true"))))
  (is (= "silly" (my-cond (nil "no") (nil) ("yes" "silly")))))

I'd really advise translating the CL over to the Clojure form of cond. I would place the mental overhead of allowing the CL syntax along with the Clojure syntax in the same project as not worth saving time in translating it now. Looking at the code in the future after becoming used to how Clojure's cond and trying to remember why the other syntax is there seems not worth the time saved by not translating.

Below version fails as Alex Taggart says below. Keeping it here so his comment makes sense. The below version does:

(defmacro my-cond [[if1 then1] & others]
  (when (or if1 then1 others)
    (let [extra-clauses# (if others `(my-cond ~@others))]
      (if then1
        `(if ~if1 ~then1 ~extra-clauses#)
        `(if ~if1 ~if1  ~extra-clauses#)))))

user> (my-cond (false "first") (nil nil) ("otherwise"))
"otherwise"


I believe the clojure version was intended to have fewer parens. You can certainly write your own cond-ish macro to do what you want.

Here is a simple implementation (i.e. does not implement the full CL version)...

(defmacro my-cond [[if1 then1] & others]
  (if others 
    `(if ~if1 ~then1 (my-cond ~@others))
    `(if ~if1 ~then1)))

And you can then...

(my-cond (false 1) (false 2) (3 3))   ; results in 3


An important feature of the CL cond is that if any of the operands of cond is a singleton, then the element within the singelton is evaluated, and if that value is non-nil, it is returned without evaluating it a second time.

(cond
   (a 100)
   ((f 1 2 3))
   (b 200))

This form evaluates to 100 if a is true, else to the value of (f 1 2 3) if that is non-nil, else to 200 if b is non-nil, else to nil.

I believe what you need is the following.

(defmacro my-cond [[if1 & then1] & others]
  (when (or if1 then1 others)
    (let [extra-clauses# (if others `(cl-cond ~@others))]
      (if then1
        `(if ~if1 (do ~@then1) ~extra-clauses#)
        `(or ~if1 ~extra-clauses#)))))
0

精彩评论

暂无评论...
验证码 换一张
取 消