计算理论(2): Common Lisp

【系列】计算理论

  1. 计算理论(1): 数学归纳法与结构归纳法
  2. 计算理论(2): Common Lisp
  3. 计算理论(3): 自动机基本入门
  4. 计算理论(4): 从NFA到DFA的子集构造详解

Common Lisp 入门与进阶指南

1. 简介

Common Lisp 是一种历史悠久且功能强大的多范式编程语言,在人工智能、符号计算以及语言研究等领域具有广泛应用价值。它支持丰富的语言特性,如宏系统 (Macro)、多重调度 (Multiple Dispatch)、函数式编程 (Lambda 等)、面向对象编程 (CLOS) 等,因而常被视为深入理解计算机语言原理与编程思想的绝佳切入点。

本指南将从最基本的符号表达式 (S-expression) 与列表操作讲起,逐步探讨局部变量、函数定义、宏展开、常用的高阶函数、哈希表使用以及可选的模式匹配库等,帮助读者建立对 Lisp 的初步到中级掌握。


2. S-表达式与符号表达

2.1 符号表达式 (S-expression)

Lisp 中一切源于 “S-expression”,其一般形式为:

(operator arg0 arg1 arg2 ...)

例如,“a ∧ (b ∨ x)”可以写作“(∧ a (∨ b x))”。
这意味着 Lisp 代码本身就是一种树状的符号结构,可由解释器或编译器读入并执行,也可以被程序本身处理或转换。

2.2 列表 (List) 与 cons

  • cons:构造一个“cons cell”,将一个元素与另一个列表结合。
    例如 (cons 1 nil) 得到 (1),而 (cons 1 (cons 2 nil)) 则得到 (1 2)。

  • list:可简化构造列表,如 (list 1 2) 与 (cons 1 (cons 2 nil)) 等价。

  • carcdr:分别用于获取列表的第一个元素和剩余部分。

    • car -> “头部”
    • cdr -> “尾部”(除去第一个元素以外的部分)

3. 基本操作与变量绑定

3.1 变量与作用域

  • Global 变量:通过 defparameter 或 defvar 定义并可在全局访问。

    (defparameter *y* 2)
    (defvar *x*)
    (setf *x* 3)
    
  • 局部变量:通过 let 或 let* 创建局部绑定。

    • let 中的绑定是并行发生的,let* 则是顺序发生。

      (let ((a 10) (b 12))
        (print (list a b))) ; => (10 12)
      
      (let* ((a 10) (b (+ a 10)))
        (print (list a b))) ; => (10 20)
      

3.2 打印与格式化输出

  • (print ):直接打印 并返回它。
  • (format t “2 + 2 = A%” 4):更灵活的字符串格式化,t 表示输出到标准输出,~A 表示以一般方式打印参数,~% 表示换行。

4. 函数定义与条件判断

4.1 函数定义

(defun function-name (arg-0 arg-1 ...)
  statement-0
  ...
  statement-n)

调用函数时,参数会被求值,然后再交给函数体使用。

示例:

(defun my-add (a b)
  (+ a b))

(my-add (* 2 3) 4) ; => 10

4.2 条件表达式

  • (if condition then-exp [else-exp])
  • (when condition body…):当条件成立时执行多个表达式,相当于 (if condition (progn body…))
  • (cond (p1 r1) (p2 r2) … (t default)):多分支选择
  • (case key ( (val1 val2) result-1 ) … (t default)):基于常量模式的多分支

示例:cond 用法

(defun sign (n)
  (cond
    ((> n 0)  1)
    ((< n 0) -1)
    (t        0)))

示例:case 用法

(defun class-duration (day)
  (case day
    ((mon wed fri) '45Min)
    ((tue thu)     '75Min)
    ((sunday)      'noClass)
    ((sat)         'maybeNoClass)
    (t             'unknown)))

5. 高阶函数 (HOF) 与 Lambda

5.1 匿名函数 Lambda

(lambda (x y) (+ x y))

可与 funcall 搭配使用:

(funcall (lambda (x y) (+ x y)) 3 4) ; => 7

5.2 常见高阶函数:map, reduce, some, every 等

  1. map

    (map 'list #'sqrt '(1 4 9))        => (1.0 2.0 3.0)
    (mapcar #'1+ '(1 2 3))             => (2 3 4)
    
  2. reduce

    (reduce #'+ '(1 2 3 4 5))          => 15
    
    (reduce (lambda (x y) (- x y)) '(3 4 5 6)) => -12
    ; 计算过程: (((3 - 4) - 5) - 6) = -12
    
  3. someevery

    (some #'evenp '(1 3 5 6 7)) => T   (因为 6 是偶数)
    (every #'evenp '(2 4 6 7))  => NIL (因为 7 不满足条件)
    
  4. find-ifremove-if

    (find-if #'evenp '(1 3 5 6 7))  => 6
    (remove-if #'evenp '(1 2 3 4 5 6 7)) => (1 3 5 7)
    

6. 宏 (Macro)

6.1 宏与函数的区别

  • 函数:参数在函数体执行前会立即求值
  • 宏:接收的不是求值后的参数,而是语法形式 (S-exp) 本身,可对之进行代码转换,返回新的 S-exp,然后再求值

6.2 宏示例

(defmacro my-unless (condition &body body)
  `(if (not ,condition)
       (progn ,@body)))

当 condition 不为真时,执行 body。

(defmacro my-if (condition then-part else-part)
  `(if ,condition ,then-part ,else-part))

接收一段代码作为 THEN 分支和 ELSE 分支,而它们在宏体中仍是原始的符号表达式。


7. 哈希表

7.1 创建与存取

(defparameter *my-table* (make-hash-table :test #'equal))

插入数据:

(setf (gethash 'apple *my-table*) "fruit")
(setf (gethash 'carrot *my-table*) "vegetable")

查询数据:

(print (gethash 'apple *my-table*))   ; => "fruit"
(print (gethash 'carrot *my-table*))  ; => "vegetable"

8. 结构体 (defstruct)

8.1 定义与使用

(defstruct person
  name
  age
  city)
  • 构造函数:make-person
  • 访问函数:person-name, person-age, person-city
  • 谓词:person-p

示例:

(let ((p1 (make-person :name "Alice" :age 30 :city "Denver")))
  (format t "Name: ~a~%" (person-name p1))
  (if (person-p p1)
      (setf (person-age p1) 31))
  (format t "Updated Age: ~a~%" (person-age p1)))

此处,我们创建了一个 person 实例 p1,然后分别读取与修改其字段。


9. 属性列表 (Property List)

9.1 使用 setf 与 getf

属性列表常以 (keyword value keyword value …) 形式出现,如:

(defparameter *person* (list :name "Alice" :age 30 :city "Denver"))
(setf (getf *person* :age) 31)
(setf (getf *person* :city) "Golden")
(setf (getf *person* :occupation) "Engineer")
(format t "Name: ~a~%" (getf *person* :name))
(format t "Occupation: ~a~%" (getf *person* :occupation))

10. 模式匹配 (Pattern Matching) —— 非内置特性

10.1 Trivia 库

Common Lisp 默认不提供模式匹配,但可以使用第三方库如 “Trivial (trivia)”。安装后可对 S-expression、列表等执行“类似 Haskell”或“类似 Prolog”风格的匹配。

(ql:quickload "trivia")

(defun exm (i)
  (trivia:match i
    ("true"  t)
    ("false" nil)
    (_       (error "invalid input"))))

或匹配数字是否奇偶:

(trivia:match i
  ((trivia:guard x (oddp x))  (format nil "~a is odd" x))
  ((trivia:guard x (evenp x)) (format nil "~a is even" x)))

10.2 匹配列表结构

(trivia:match i
  ((list)   "the list is empty")
  ((list a) (format nil "one element: ~a" a))
  ((list a b) (format nil "two elements: ~a, ~a" a b))
  (_ "more elements"))

若要遍历列表,将所有元素相加,可在匹配到空表时返回 0,其余递归处理 car 与 cdr。


11. 与 SBCL、Quicklisp 配合使用

11.1 安装与环境

  • 安装 SBCL:

    sudo apt-get install sbcl
    
  • 获取 Quicklisp:

    wget https://beta.quicklisp.org/quicklisp.lisp
    sbcl --load quicklisp.lisp \
         --eval '(quicklisp-quickstart:install)' \
         --eval '(ql-util:without-prompting (ql:add-to-init-file))'
    
  • 启动 SBCL 并加载常用库:

    sbcl --eval '(ql:quickload "alexandria")'
    

11.2 常用参考

  • https://lisp-lang.org/learn/getting-started/
  • Common Lisp HyperSpec:
    https://www.lispworks.com/documentation/HyperSpec/Front/Contents.htm

12. 总结

通过以上内容,我们对 Common Lisp 中的核心概念和工具有了一个初步的认识,包括:

  • S-expression 与 列表操作:Lisp 的根基
  • 局部/全局变量绑定:let、defvar、defparameter
  • 常见函数与宏:defun、defmacro
  • 高阶函数 (HOF) 与 匿名函数 (lambda):map、reduce、some、every 等
  • 哈希表 (hash-table) 和 结构体 (defstruct):对复杂数据的管理
  • 属性列表:快速记录与存储键值对
  • 模式匹配:使用第三方库进行更灵活的结构分析
  • 与 SBCL、Quicklisp 配合:基本环境配置与使用

Common Lisp 拥有强大的宏系统和灵活的多范式编程风格,如果你继续深入学习,还可探究 CLOS(Common Lisp Object System)以及更多高级特性。祝你在 Lisp 世界里玩得愉快!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

跑起来总会有风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值