Lisp概述

Lisp历史

《黑客与画家》

Lisp的本质

The Nature Of Lisp

S表达式

我们知道任何语言的源码都可以转换成XML,例如:

  1. <task name="Test">
  2. <echo message="Hello World" />
  3. </task>
  4. <Test />
前缀表示法

把XML的尖括号换成圆括号(S表达式):

  1. (task (name "Test")
  2. (echo (message "Hello World")))
  3. (Test)
后缀表示法
  1. class test(name:"test") {
  2. echo(message="Hello World")
  3. }

Lisp程序是由三个基本构建块:

  • atom
  • list
  • string

计算LISP程序有两部分:

  • 读取器:将程序文本转换成Lisp对象
  • 求值器:语言的语义在这些对象中的条款执行求值程序

LISP特性

  • Lisp实际上是一个函数调用f(x)为 (f x),例如 cos(45)被写入为 (cos 45)
  • LISP表达式是不区分大小写的,cos 45 或COS 45是相同的。
  • LISP尝试计算一切,包括函数的参数。只有三种类型的元素是常数,总是返回自己的值:
    • 数字
    • 字母t,即表示逻辑真
    • 该值为nil,这表示逻辑false,还有一个空的列表。

Hello World

  1. (format t "hello world")
  2. (defun hi ()
  3. (format t "你好,世界!"))
  4. (hi)
  5. (funcall #'hi)
  6. (apply #'hi nil)
  7. (load "/work/lisp/hi.lisp" :external-format :utf-8)
  8. (hi)

t 输出到控制台

toplevel 顶层

Read-eval-print-loop

表达式

所有的 Lisp 表达式,要么是 1 这样的数原子,要么是包在括号里,由零个或多个表达式所构成的列表。

原子表达式

  1. 1
  2. "ss"

数字 1 称之为对自身求值。

前序表达式

  1. (+ 2 3 4)
  • +操作符
  • 2 3 4 实参, + 可以接受任意数量的实参

由于操作符可接受不定数量的实参,我们需要用括号来标明表达式的开始与结束。

求值

函数求值规则

函数调用的求值方式。由左至右对实参求值,将它们的数值传入函数,来返回整个表达式的值。这称为 Common Lisp 的求值规则。

  1. (+ 2 3)
  2. (+ 2 (+ 2 3))
  1. 首先从左至右对实参求值。
    在这个例子当中,实参对自身求值,所以实参的值分别是 2 跟 3 。
  2. 实参的值传入以操作符命名的函数。
    在这个例子当中,将 2 跟 3 传给 + 函数,返回 5 。
  3. 如果实参本身是函数调用的话,上述规则同样适用。

但不是所有的 Common Lisp 操作符都是函数,不过大部分是。

不遵守求值规则(宏)

quote

quote 操作符接受一个实参,并完封不动地返回它。
quote 作为一种保护表达式不被求值的方式。

  1. (quote (+1 2))
  2. '(+ 1 2)

数据类型

变量没有类型,数值才有类型。
你可以想像每个对象,都贴有一个标明其类型的标签。这种方法叫做显式类型(manifest typing)。

  • 整数(integer)
  • 字符串(string)
  • 符号(symbol)
    符号是英语的单词。无论你怎么输入,通常会被转换为大写
    1. 'add
    2. '(my 3 "Sons")
  • 列表(lists)
    空列表
    1. nil
    2. ()
  • 布尔( 真&假)
    • 真 t
    • 假 nil
      1. (not nil)

predicate 函数

以p结尾

  • listp
  • numberp
  • consp
  • zerop 是否为0

nil

    1. ()
    1. (not nil)

if

  1. (if (条件表达式)
  2. (条件为真执行)
  3. (条件为假执行)
  4. )

列表操作

  • cons
    cons函数构造列表
  • car
  • cdr

  • remove

    1. (setf lst '(c a r a t))
    2. (remove 'a lst)
  • dolist
    1. (let ((lst (list 'a 'b 'c)))
    2. (dolist (obj lst)
    3. (format t "~A" obj)))
  • copy-list
  • append

  • nth

    1. > (nth 0 '(a b c))
    2. A
  • nthcdr
    1. (nthcdr 2 '(a b c))
    2. (C)
  • last
    1. > (last '(a b c))
    2. (C)
  • Common Lisp 定义了函数 first 直到 tenth 可以取得列表对应的元素。这些函数不是 零索引的 (zero-indexed):
    (second x) 等同于 (nth 1 x) 。

    IO

    load

    1. (load "compress.lisp")

    format

    1. (format t "~A plus ~A equals ~A. ~%" 2 3 (+ 2 3))

第一个实参 t ,表示输出被送到缺省的地方去。通常是顶层。
第二个实参 是一个用作输出模版的字符串。

  • ~A 表示了被填入的位置,
  • ~% 表示一个换行。这些被填入的位置依序由后面的实参填入。

函数

函数式编程意味着撰写利用返回值而工作的程序,而不是修改东西。

  1. (defun our-third (x)
  2. (car (cdr (cdr x))))

函数 作为 参考

引用函数

  1. (function +)
  2. #'+

通过引用执行函数

  1. (apply #'+ '(1 2 3))
  2. (apply #'+ 1 2 '(3 4 5))
  3. (funcall #'+ 1 2 3)

lambda函数

  1. (lambda (x) (+ x 100))

变量

  • 局部变量
    1. (let ((x 1) (y 2))
    2. (+ x y))
  • 全局变量
    1. (defparameter *glob* 99)
  • 全局常量
    1. (defconstant limit (+ *glob* 1))
  • 是否存在全局变量/常量
    1. (boundp '*glob*)
  • 赋值
    1. (setf *glob* 98)
    如果 setf 的第一个实参是符号(symbol),且符号不是某个局部变量的名字,则 setf 把这个符号设为全局变量。
    1. (setf x (list 'a 'b 'c))
    2. (setf (car x) 'n)
    3. (setf a 'b
    4. c 'd
    5. e 'f)

    相等

    1. (equal "ss" "ss") ; t, 内容相等
    2. (eql "ss" "ss") ; nil,同一对象

迭代 (Iteration)

  1. (do ((variable initial update))
  2. ())
  3. (do ((i 1 (+ i 1)))
  4. ((> i 10) 'done)
  5. (format t "~A" i))

  1. (progn
  2. (format t "~A ~A~%" 1 2)
  3. )
文档更新时间: 2019-06-18 10:44