store/[org.clojure/clojure "1.8.0"] clj::clojure.core/case

Community Documentation

Edit

Arities

[e & clauses]

Docstring

Takes an expression, and a set of clauses. Each clause can take the form of either:

test-constant                        result-expr
(test-constant1 ... test-constantN)  result-expr

The test-constants are not evaluated. They must be compile-time literals, and are implicitly quoted. If the expression is equal to any of the listed test-constant, the corresponding result-expr is returned. A single default expression can follow the clauses, and its value will be returned if no clause matches. If no default expression is provided and no clause matches, an IllegalArgumentException is thrown.

Unlike clj::clojure.core/cond and clj::clojure.core/condp, clj::clojure.core/case does a constant-time dispatch, the clauses are not considered sequentially. All manner of constant expressions are acceptable in case, including numbers, strings, symbols, keywords, and (Clojure) composites thereof. Note that since lists are used to group multiple constants that map to the same expression, a vector can be used to match a list if needed. The test-constants need not be all of the same type.

Source

(defmacro case 
  "Takes an expression, and a set of clauses.

  Each clause can take the form of either:

  test-constant result-expr

  (test-constant1 ... test-constantN)  result-expr

  The test-constants are not evaluated. They must be compile-time
  literals, and need not be quoted.  If the expression is equal to a
  test-constant, the corresponding result-expr is returned. A single
  default expression can follow the clauses, and its value will be
  returned if no clause matches. If no default expression is provided
  and no clause matches, an IllegalArgumentException is thrown.

  Unlike cond and condp, case does a constant-time dispatch, the
  clauses are not considered sequentially.  All manner of constant
  expressions are acceptable in case, including numbers, strings,
  symbols, keywords, and (Clojure) composites thereof. Note that since
  lists are used to group multiple constants that map to the same
  expression, a vector can be used to match a list if needed. The
  test-constants need not be all of the same type."
  {:added "1.2"}

  [e & clauses]
  (let [ge (with-meta (gensym) {:tag Object})
        default (if (odd? (count clauses)) 
                  (last clauses)
                  `(throw (IllegalArgumentException. (str "No matching clause: " ~ge))))]
    (if (> 2 (count clauses))
      `(let [~ge ~e] ~default)
      (let [pairs (partition 2 clauses)
            assoc-test (fn assoc-test [m test expr]
                         (if (contains? m test)
                           (throw (IllegalArgumentException. (str "Duplicate case test constant: " test)))
                           (assoc m test expr)))
            pairs (reduce1
                       (fn [m [test expr]]
                         (if (seq? test)
                           (reduce1 #(assoc-test %1 %2 expr) m test)
                           (assoc-test m test expr)))
                       {} pairs)
            tests (keys pairs)
            thens (vals pairs)
            mode (cond
                   (every? #(and (integer? %) (<= Integer/MIN_VALUE % Integer/MAX_VALUE)) tests)
                   :ints
                   (every? keyword? tests)
                   :identity
                   :else :hashes)]
        (condp = mode
          :ints
          (let [[shift mask imap switch-type] (prep-ints tests thens)]
            `(let [~ge ~e] (case* ~ge ~shift ~mask ~default ~imap ~switch-type :int)))
          :hashes
          (let [[shift mask imap switch-type skip-check] (prep-hashes ge default tests thens)]
            `(let [~ge ~e] (case* ~ge ~shift ~mask ~default ~imap ~switch-type :hash-equiv ~skip-check)))
          :identity
          (let [[shift mask imap switch-type skip-check] (prep-hashes ge default tests thens)]
            `(let [~ge ~e] (case* ~ge ~shift ~mask ~default ~imap ~switch-type :hash-identity ~skip-check))))))))

Example 1

Edit
(let [mystr "hello"]
  (case mystr
    "" 0
    "hello" (count mystr)))
;; => 5

(let [mystr "no match"]
  (case mystr
    "" 0
    "hello" (count mystr)))
;; => No matching clause: no match [Thrown class java.lang.IllegalArgumentException]

(let [mystr "no match"]
  (case mystr
    "" 0
    "hello" (count mystr)
    "default"))
;; => "default"

;; You can give multiple values for the same condition by putting
;; those values in a list.
(case 'y
  (x y z) "x, y, or z"
  "default")
;; => "x, y, or z"

(let [myseq '(1 2)]
  (case myseq
    (()) "empty seq"
    ((1 2)) "my seq"
    "default"))
;; => "my seq"

;; "The test-constants are not evaluated.They must be compile-time
;; literals, and need not be quoted." 
(let [myvec [1 2]]
  (case myvec
    [] "empty vec"
    (vec '(1 2)) "my vec"
    "default"))
;; => "default"

Uses on crossclj