ClojureDocs

Nav

Namespaces

case

clojure.core

Available since 1.2 (source)
  • (case e & clauses)
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.
8 Examples
(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"
;; From: The Clojure Style Guide 
;;  https://github.com/bbatsov/clojure-style-guide#case
;;  License: Creative Commons Attribution 3.0 Unported License
;;
;; Prefer case instead of cond or condp when test expressions are compile-time constants.

;; good
(cond
  (= x 10) :ten
  (= x 20) :twenty
  (= x 30) :forty
  :else :dunno)

;; better
(condp = x
  10 :ten
  20 :twenty
  30 :forty
  :dunno)

;; best
(case x
  10 :ten
  20 :twenty
  30 :forty
  :dunno)
;; test-constants are not evaluated, so the symbols should NOT be quoted
(let [x 'a-symbol]     ; quoted
  (case x 
    a-symbol :a        ; not quoted
    b-symbol :b))      ; not quoted
;;=> :a
;; Other example on
;; "The test-constants are not evaluated. They must be compile-time
;; literals, and need not be quoted." 

(def one 1)
;;=> #'user/one
(case 1 
  one :one 
  :not-one)
;;=> :not-one
;; You can use a macro to inline test-constants you wish to define separately.

(def ops-single '("=" "!=" "<" "<=" ">" ">="))

(def ops-multi '("ONE OF" "NONE OF"))

(defmacro ops-type [op]
  `(case ~op
     ~ops-single :single
     ~ops-multi :multi))
(case :blocking
  (:blocking :compute) :block-or-compute
  :async :async)

;;=> :block-or-compute


(case :async
  (:blocking :compute) :block-or-compute
  :async :async)

;;=> :async
;; I timed rmprescott's example:

(let [x 30] 
  ;; "good"
  (time (cond
          (= x 10) :ten
          (= x 20) :twenty
          (= x 30) :thirty
          :else :dunno))     ;;=> "Elapsed time: 0.0666 msecs"

  ;; "better"
  (time (condp = x
          10 :ten
          20 :twenty
          30 :thirty
          :dunno))           ;;=> "Elapsed time: 0.4197 msecs"

  ;; best in performance if else statement is executed (not shown)
  ;; best in readability
  (time (case x
          10 :ten
          20 :twenty
          30 :thirty
          :dunno))           ;;=> "Elapsed time: 0.0032 msecs"
 
  ;; best in performance if known branches are executed
  ;; worst in readability
  (time (if (= x 10)
          :ten
          (if (= x 20)
            :twenty
            (if (= x 30)
              :thirty
              :dunno))))))   ;;=> "Elapsed time: 0.0021 msecs" 

;;=> :thirty
;; demonstrates inclusion on the test
;; ... could be one of "what" or "why"

(case "what"
    ("what" "why") :question
    :something-else)

;;=> :question
See Also

Takes a set of test/expr pairs. It evaluates each test one at a time. If a test returns logical t...

Added by gstamp

Takes a binary predicate, an expression, and a set of clauses. Each clause can take the form of ei...

Added by gstamp
5 Notes
    By , created 14.4 years ago, updated 14.4 years ago

    the third example describing myseq may be incorrect. I think we have to use vectors for comparing list of compile time constants.

    (let [myseq [1 2]]
     (case myseq
      [] "empty seq"
      [1 2] "my seq"
      "default"))
    

    The parenthesized test conditions are used when multiple test conditions give the same output (output expression). In my case , since both [true, true] and [false, false] return true, i have put them within parenthesis.

    (defn equ
     [a b]
     (case [a b]
       ([true true] [false false]) true
       true))
    
    (equ true true) ;; returns true
    (equ false false) ;; returns true
    (equ false true) ;; return false
    (equ true false) ;; returns false
    
    By , created 14.4 years ago

    I updated that example with myseq to reflect behavior of Clojure 1.2.0 on my machine. I also added an additional example to explicitly demonstrate multiple values for the same condition.

    By , created 4.2 years ago, updated 4.2 years ago

    Java enums are not compile-time literals.

    By , created 3.0 years ago, updated 3.0 years ago

    Keep in mind this is not quite like pattern matching.

    (case [:a :b]
      [:a :b] "a b combo"
      [_ :b] "at least b" 
    )
    

    Will not work. Instead install and use core.match:

    (clojure.core.match/match [:a :b]
      [:a :b] "a b combo"
      [_ :b] "at least b")
    

    (Thanks to potetm and Martin Půda on Clojurians Slack)

    By , created 2.0 years ago

    Reminder: compile-time literals are not the same thing as compile-time constants. The Clojure macro/compiler is not smart enough to work with things like Java constants or ^:const vars.

    E.g.:

    (case Math/PI
      Math/PI :pi
      Math/E :e
      :default)
    => :default
    
    
    
    (def ^:const pi Math/PI)
    => #'user/pi
    (case Math/PI
      pi :pi
      Math/E :e
      :default)
    => :default
    

    will not work.