条件处理

if 这个special form跟java里面的if的语义是一样的, 它接受三个参数, 第一个是需要判断的条件,第二个表达式是条件成立的时候要执行的表达式,第三个参数是可选的,在条件不成立的时候执行。如果需要执行多个表达式,那么把多个表达式包在do里面。看例子:

(import '(java.util Calendar GregorianCalendar))
(let [gc (GregorianCalendar.)
      day-of-week (.get gc Calendar/DAY_OF_WEEK)
      is-weekend (or (= day-of-week Calendar/SATURDAY) (= day-of-week Calendar/SUNDAY))]
  (if is-weekend
    (println "play")
    (do (println "work")
        (println "sleep"))))

whenwhen-not 提供和if类似的功能, 只是它们只在条件成立(或者不成立)时候执行一个表达式。另一个不同是,你可以执行任意数目的表达式而不用用do把他们包起来。

(when is-weekend (println "play"))
(when-not is-weekend (println "work") (println "sleep"))

if-let 把一个值bind到一个变量,然后根据这个binding的值来决定到底执行哪个表达式。下面的代码会打印队列里面第一个等待的人的名字,或者打印“no waiting”如果队列里面没有人的话。

(defn process-next [waiting-line]
  (if-let [name (first waiting-line)]
    (println name "is next")
    (println "no waiting")))

(process-next '("Jeremy" "Amanda" "Tami")) ; -> Jeremy is next
(process-next '()) ; -> no waiting

when-let 宏跟 if-let 类似, 不同之处跟上面 ifwhen 的不同之处是类似的。 他们没有else部分,同时还支持执行任意多个表达式。比如:

(defn summarize
  "prints the first item in a collection
  followed by a period for each remaining item"
  [coll]
  ; Execute the when-let body only if the collection isn't empty.
  (when-let [head (first coll)]
    (print head)
    ; Below, dec subtracts one (decrements) from
    ; the number of items in the collection.
    (dotimes [_ (dec (count coll))] (print \.))
    (println)))

(summarize ["Moe" "Larry" "Curly"]) ; -> Moe..
(summarize []) ; -> no output

condp 宏跟其他语言里面的switch/case语句差不多。它接受两个参数,一个谓词参数 (通常是 = 或者 instance? ) 以及一个表达式作为第二个参数。在这之后,它接受任意数量的值-表达式的对子,这些对子会按顺序evaluate。如果谓词的条件跟某个值匹配了, 那么对应的表达式就被执行。一个可选的最后一个参数可以指定, 这个参数指定如果一个条件都不符合的话, 那么就返回这个值。如果这个值没有指定,而且没有一个条件符合谓词, 那么一个 IllegalArgumentException 异常就会被抛出。

下面的例子让用户输入一个数字,如果用户输入的数字是1,2,3,那么程序会打印这些数字对应的英文单词。否则它会打印”unexpected value”。在那之后,它会测试一个本地binding的类型,如果是个数字它会打印这个数字乘以2的结果;如果是字符串, 那么打印这个字符串的长度乘以2的结果。

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

cond 宏接受任意个 谓词/结果表达式 的组合。它按照顺序来测试所有的谓词,直到有一个谓词的测试结果是true, 那么它返回其所对应的结果。如果没有一个谓词的测试结果是true, 那么会抛出一个 IllegalArgumentException 异常。通常最后一个谓词一般都是true, 以充当默认情况。

下面的例子让用户输入水的温度, 然后打印出水的状态: 是冻住了,还是烧开了,还是一般状态。

(print "Enter water temperature in Celsius: ") (flush)
(let [reader (java.io.BufferedReader. *in*)
      line (.readLine reader)
      temperature (try
        (Float/parseFloat line)
        (catch NumberFormatException e line))] ; use string value if not float
  (println
    (cond
      (instance? String temperature) "invalid temperature"
      (<= temperature 0) "freezing"
      (>= temperature 100) "boiling"
      true "neither")))