The Little Schemer是一部很有名的Lisp书,其中的思想颇具禅意,令我受益匪浅。
它的作者也很有名,Daniel Friedman。可谓宗师级的人物,王垠就是他的学生之一,有兴趣的朋友可以去了解一下。从王垠记录的一些趣事上来看,他是一位老顽童般授人以渔的教授。从这本书那种启发你思考的风格,也可以看出一二。
;;; learning The Little Schemer
;; Chapter 1.
(def car first)
(def cdr rest)
(def cons clojure.core/cons)
(def eq? =)
(def list? clojure.core/seq?)
(def atom?
(fn [x]
(not (list? x))))
(def s-exp?
(fn [x]
(or (atom? x) (list? x))))
(def null?
(fn [x]
(and (list? x) (empty? x))))
要小心的去理解上面 atom? 和 null? 的语义,要点是分开 nil 与空表 '()
我们不期望在这些操作中检测与引入nil语义,我们说一个东西要么是一个原子,要么是一个表(可以是空表)。
Clojure默认在一些情况下将nil和空表等同起来,有时又不同,比如
> (cons nil nil)
(nil)
> (cons '() nil)
(())
> (cons nil '())
(nil)
> (cons '() '())
(())
;;我的理解是:当参数位置期望是一个表时,nil被当作空表,当期望的是一个原子时,nil作为原子
这样做是符合惯例,事实上遗传自CommonLisp和Java的血统所以不可避免这一点。
你可以这样理解,Scheme世界有逻辑真值,逻辑假值,空表,但没有 nil 这个东西。
由于是第一章内容,所以采用基本的两个形式 def 和 fn 来写。
从第二章开始全部defn,这样递归时看起来会比较美观
;; Chapter 2.
(defn lat? [l]
(cond (null? l) true
(atom? (car l)) (lat? (cdr l))
:else false))
(defn member? [a lat]
(cond (null? lat) false
:else (or (eq? (car lat) a)
(member? a (cdr lat)))))
可以看到,由于一些语法糖和良好的设计,Clojure代码外观比Scheme或CommonLisp都要更美观一些。
;一开始先这样按照思考的步骤写出来
(defn rember [a lat]
(cond
(null? lat) '()
:else (cond
(eq? (car lat) a) (cdr lat)
:else (cons (car lat)
(rember a (cdr lat))))))
我的缩进看起来太丑?看到这种 cond 和 else 阶梯状排列的代码,老师会告诉我可以将其排版的更好一些:
;; Chapter 3.
(defn rember [a lat]
(cond
(null? lat) '()
(eq? (car lat) a) (cdr lat)
:else (cons (car lat)
(rember a (cdr lat)))))
(defn firsts [l]
(cond
(null? l) '()
:else (cons (car (car l))
(firsts (cdr l)))))
(defn insertR [new old lat]
(cond
(null? lat) '()
(eq? old (car lat)) (cons old
(cons new (cdr lat)))
:else (cons (car lat)
(insertR new old (cdr lat)))))
(defn insertL [new old lat]
(cond
(null? lat) '()
(eq? old (car lat)) (cons new
(cons old (cdr lat)))
:else (cons (car lat)
(insertL new old (cdr lat)))))
当我能跟着书里规则自然的写代码时,我已经开始明白该如何递归思考了:)
第四章太简单,直接进入下一章~
;; Chapter 4.
(def add1 clojure.core/inc)
(def sub1 clojure.core/dec)
(def zero? clojure.core/zero?)
;; Chapter 5.
(defn rember* [a l]
(cond
(null? l) '()
(atom? (car l)) (cond
(eq? a (car l)) (rember* a (cdr l))
:else (cons (car l) (rember* a (cdr l))))
:else (cons (rember* a (car l))
(rember* a (cdr l)))))
(defn occur* [a l]
(cond
(null? l) 0
(atom? (car l)) (cond
(eq? a (car l)) (add1 (occur* a (cdr l)))
:else (occur* a (cdr l)))
:else (+ (occur* a (car l))
(occur* a (cdr l)))))