(defparameter *words* (make-hash-table :size 10000))
(defconstant maxword 100)
(defun punc (c)
(case c
(#\. '|.|) (#\, '|,|) (#\; '|;|)
(#\! '|!|) (#\? '|?|)))
;;当前的字符串为symb,而它前面的字符串为prev,首先判断是否有如下个(prev . (symb . n))如果有的话,n+1,没有就
;;添加一位(prev . (symb . n)),在最后会把当前的symb设置为prev,因为
(let ((prev '|.|))
(defun see (symb)
(let ((pair (assoc symb (gethash prev *words*))))
(if (null pair)
(push (cons symb 1) (gethash prev *words*)) ;;如果没有的话,就在相应位置赋值
(incf (cdr pair))))
(setf prev symb))) ;语法闭包,只要外面有这个函数的引用,它里面的值就可以访问.因为这样你才能够把当前处理过的作为prev并且在下一次比较时能够引用到。因为我们没有如c语言中你在函数外面创建一个变量,每一次我都可以通过它作为一个中转站来存储值,并且用于下一次的比较。忘了c++中如果想调用上一次执行中的值该是如何实现?是定义一个toplevel的变量来接收吗?还是跟这个一样,语法闭包,只要有这个函数的引用它里面的值就可以利用。
(defun read-text (pathname)
(with-open-file (s pathname :direction :input)
(let ((buffer (make-string maxword)) ;定义一个buffer大小为10000
(pos 0))
(do ((c (read-char s nil :eof)
(read-char s nil :eof)))
((eql c :eof))
;;第一progn就是实现一个单词的整体输入,因为当碰见分隔符的时候才写入停止。
(if (or (alpha-char-p c) (char= c #\'))
(progn ;如果是字符或者'的话就直接写入buffer
(setf (aref buffer pos) c) ;设置buffer在pos的值为C
(incf pos)) ;因为上面已经添加了值,所以buffer的pos+1
(progn
(unless (zerop pos) ;如果不是起始位置
(see (intern (string-downcase ;变为小写
(subseq buffer 0 pos)))) ;取从0到pos的子串
(setf pos 0)) ;比如see过half以后,C为空格,执行下面一段逻辑为nil
(let ((p (punc c))) ;为了处理那些连续的标点符号比如,;.
(if p (see p)))))))))
;;;n是总共就循环这么多次,首先我们先给他一个prev,也就是key,然后你根据这个key去调用random-next.意思就是
;;;随机选出来一个它的value中的car做为下一个后继,然后再用这个后继重复前面的操作。总共次数是n.
(defun random-next (prev)
(let* ((choices (gethash prev *words*));;把prev的value都放到choices里面
(i (random (reduce #'+ choices ;;把choices这些个alist的累加后面的cdr,得到一个随机值
:key #'cdr))))
(dolist (pair choices) ;例如(gethash '|d| *words*) ((|in| . 2) (|round| . 2) (|,| . 2))
(if (minusp (decf i (cdr pair)));;如果choices中alist对象car大于随机值,就返回它的key.
(return (car pair))))))
(defun generate-text (n &optional (prev '|.|))
(if (zerop n)
(terpri) ;换行,如果已经是行的开头的话,没有任何效果
(let ((next (random-next prev)))
(format t "~A " next)
(generate-text (1- n) next))))

CL-USER> (assoc :a (list (cons :a 1) (cons :b 2)))
(:A . 1)
CL-USER> (intern "help") ;;它为啥要用这种形式呢?
|help|
CL-USER> (incf (cdr (cons :a 1)))
2
CL-USER> (aref "string" 2)
#\r
CL-USER> (subseq "string" 1 3)
"tr"
CL-USER> (defun test(n)
(unless (< n 4)
(format t "unless1")
(format t "unless2")))
TEST
CL-USER> (test 6)
unless1unless2
NIL
CL-USER> (reduce #'+ (list (cons 2 3) (cons 4 5 )) :key #'cdr)
8
(see (intern (string-downcase
(subseq buffer 0 pos))))
是否可以变为下面这个形式呢?
(see (subseq buffer 0 pos))
因为hash-table是按eql进行相等检查的,如果你用第二种方式,键与我们输入的形式就永远不会相等。