Clojure括号问题一则
把下面的common lisp代码用Clojure来写
(defun qsort (l) (cond ((null l) nil) (t (append (qsort (list< (car l) (cdr l))) (cons (car l) nil) (qsort (list>= (car l) (cdr l))))))) (defun list< (a l) (cond ((or (null a)(null l)) nil) (( < a (car l)) (list< a (cdr l))) (t (cons (car l) (list< a (cdr l)))))) (defun list>= (a l) (cond ((or ( null a)(null l)) nil) (( >= a (car l)) (list>= a (cdr l))) (t (cons (car l) (list>= a (cdr l))))))
结果硬是写成了下面这样
(defn list< [a l] (println l) (filter #(< % a) l) ) (defn list>= [a l] (filter #(>= % a) l) ) (defn qsort [l] ;(println l) (cond (empty? l) '()) :otherwise (concat (qsort (list< (first l) (rest l))) (cons (first l) nil) (qsort (list>= (first l) (rest l)))) )
qsort中cond的第一条语句后面 多了一个右括号, 提前结束了cond宏, 输入'()的时候会导致死循环, 因为这里即使提前结束cond, 但是总的括号数量正确的话, 仍然会是合法的代码. 实际变成了
(defn qsort [l] ;(println l) (cond (empty? l) '()) :otherwise (concat (qsort (list< (first l) (rest l))) (cons (first l) nil) (qsort (list>= (first l) (rest l))) ) )
而肉眼则完全没有察觉出来, 导致我一开始还怀疑Clojure的cond宏的实现和common lisp有所不同. 我想的是, cond中没有命中的条件中, 如果有递归调用, 那么递归调用仍然会执行. 实际上cond宏就是封装了嵌套的if语句而已, 没有命中条件的语句绝对不会执行的.
而且这样的错误写法并不是第一次碰到, 上次在某个地方遇到了相同的问题. 在LISP当中, 也算是一种bug的来源, 那就是括号匹配明明错了, 但是代码上看不出来, 而且仍然是合法的代码, 可以执行, 但是会出现奇怪的错误, 一般情况下又不会想到是括号写错了, 这种情况往往意味着长时间的debug.