condp

added
1.0

ns
clojure.core

type
macro

(condp pred expr & clauses)

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

test-expr result-expr

test-expr :>> result-fn

Note :>> is an ordinary keyword.

For each clause, (pred test-expr expr) is evaluated. If it returns
logical true, the clause is a match. If a binary clause matches, the
result-expr is returned, if a ternary clause matches, its result-fn,
which must be a unary function, is called with the result of the
predicate as its argument, the result of that call being the return
value of condp. 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.

                ;; Taken from the excellent clojure tutorial:
;; http://java.ociweb.com/mark/clojure/article.html

(print "Enter a number: ")
(flush) ; stays in a buffer otherwise
(let [line (read-line)
      value (try
              (Integer/parseInt line)
              (catch NumberFormatException e line))] ; use string val if not int
  (println
    (condp = value
      1 "one"
      2 "two"
      3 "three"
      (str "unexpected value, \\"" value \\")))
  (println
    (condp instance? value
       Number (* value 2)
       String (* (count value) 2))))

            
                ;; (some #{4 5 9} [1 2 3 4]) 
;;  is the first matching clause, 
;;  the match value is 4 which is decremented
(condp some [1 2 3 4]
  #{0 6 7} :>> inc
  #{4 5 9} :>> dec
  #{1 2 3} :>> #(+ % 3))
;;=> 3
            
                ;; in this case there is no matching clause
;; so an exception is raised.
(condp some [1 2 3 4]
   #{0 6 7} :>> inc
   #{5 9}   :>> dec)

;; java.lang.IllegalArgumentException: No matching clause: [1 2 3 4]
            
                ;; a composite predicate which parses a string with "re-seq" 
;; producing a list which is made into a "seq".
(condp (comp seq re-seq) "foo=bar"
  #"[+](\\w+)"    :>> #(vector (-> % first (nth 1) keyword) true)
  #"[-](\\w+)"    :>> #(vector (-> % first (nth 1) keyword) false)
  #"(\\w+)=(\\S+)" :>> #(let [x (first %)]
                        [(keyword (nth x 1)) (nth x 2)]))
;;=> [:foo "bar"]
            
                ;; See examples for "if" explaining Clojure's idea of logical true
;; and logical false.
            
                ;;this is with liberator
;;branching on request method
(defresource my-resource
  :exists? (fn [{:keys [db] {query-params :query-params 
                             body :body 
                             method :request-method} 
                 :request}]
             
             (condp = method
               :get (my-get-exists-fn)
               :post (my-post-exists-fn))))
            
                ;; a recursive function to calculate length
;; same as 'count'
(defn length [lst]
    (condp = lst
        (list) 0 ; if empty list result 0
        (+ 1 (length (rest lst))))) ; default expression

(length '(1 2 3))
;;=> 3
            
                ;; pass in the function
;; #(%1 2 %2) < 3 
;; is the passing condition.
(condp #(%1 2 %2) 3
  = "eq"
  < "lt"
  > "gt")
;;=> "lt"

;; test argument against various predicates
(condp #(%1 %2) :foo
  string? "it's a string"
  keyword? "it's a keyword"
  symbol? "it's a symbol"
  fn? "it's a function"
  "something else!")
;;=> "it's a keyword"
            
                ; This function is part of a great solution to the "fizzbuzz" interview question.
; Let's review 
; Players take turns to count incrementally, 
; replacing any number divisible by three with the word "fizz",  
; any number divisible by five with the word "buzz", and 
; any number divisible by both three and five with the word "fizzbuzz".
(defn fizz-buzz [n]
  (condp #(zero? (mod %2 %1)) n
    15 "fizzbuzz"
    3  "fizz"
    5  "buzz"
    n))

(into [] (map fizz-buzz) (range 1 20))
;;=> [1 2 "fizz" 4 "buzz" "fizz" 7 8 "fizz" "buzz" 
;;    11 "fizz" 13 14 "fizzbuzz" 16 17 "fizz" 19]
            
                ;; Test a string against multiple regexps, and do something different
;; with the match each time. 
(condp re-matches "17->42"
  #"(\\w+)->(\\w+)" :>> (fn [[_ p1 p2]]
                        {:start p1 :end p2})

  #"(\\w+)->$" :>> (fn [[_ p1]]
                    {:start p1})

  #"\\w+" :>> (fn [[p]]
               {:fixed p})

  nil)
; => {:start "17" :end "42"}