LISP - 快速指南


LISP - 概述

约翰·麦卡锡 (John McCarthy) 在 FORTRAN 开发后不久于 1958 年发明了 LISP。它首先由 Steve Russell 在 IBM 704 计算机上实现。

它特别适合人工智能程序,因为它可以有效地处理符号信息。

Common Lisp 起源于 20 世纪 80 年代和 90 年代,试图统一 Maclisp 后继者的几个实现小组的工作,例如 ZetaLisp 和 NIL(Lisp 的新实现)等。

它作为一种通用语言,可以轻松扩展以实现特定的实现。

用 Common LISP 编写的程序不依赖于机器特定的特征,例如字长等。

通用LISP的特点

  • 它是独立于机器的

  • 它采用迭代设计方法,并且易于扩展。

  • 它允许动态更新程序。

  • 它提供高级调试。

  • 它提供了高级的面向对象编程。

  • 它提供了一个方便的宏系统。

  • 它提供了广泛的数据类型,例如对象、结构、列表、向量、可调整数组、哈希表和符号。

  • 它是基于表达的。

  • 它提供了一个面向对象的条件系统。

  • 它提供了完整的I/O库。

  • 它提供了广泛的控制结构。

LISP 内置应用程序

用 Lisp 构建的大型成功应用程序。

  • Emacs

  • G2

  • 计算机辅助设计

  • 伊戈尔雕刻师

  • 雅虎商店

LISP - 环境设置

本地环境设置

如果您仍然愿意为 Lisp 编程语言设置环境,则您的计算机上需要有以下两个软件:(a) 文本编辑器和 (b) Lisp 执行器。

文本编辑器

这将用于输入您的程序。少数编辑器的示例包括 Windows 记事本、操作系统编辑命令、Brief、Epsilon、EMACS 和 vim 或 vi。

文本编辑器的名称和版本可能因不同操作系统而异。例如,记事本将在 Windows 上使用,vim 或 vi 可以在 Windows 上使用,也可以在 Linux 或 UNIX 上使用。

您使用编辑器创建的文件称为源文件,包含程序源代码。Lisp 程序的源文件通常以扩展名“ .lisp ”命名。

在开始编程之前,请确保您有一个文本编辑器,并且您有足够的经验来编写计算机程序,将其保存在文件中,最后执行它。

Lisp 执行器

源文件中编写的源代码是程序的人类可读源。它需要被“执行”,转变成机器语言,这样你的CPU才能真正按照给定的指令执行程序。

这种 Lisp 编程语言将用于将源代码执Behave最终的可执行程序。我假设您有编程语言的基本知识。

CLISP 是 GNU Common LISP 多架构编译器,用于在 Windows 中设置 LISP。windows版本在windows下使用MingW模拟unix环境。安装程序会处理此问题并自动将 clisp 添加到 Windows PATH 变量中。

您可以从此处获取适用于 Windows 的最新 CLISP - https://sourceforge.net/projects/clisp/files/latest/download

列出环境设置

默认情况下,它在“开始”菜单中为逐行解释器创建一个快捷方式。

如何使用 CLISP

在安装过程中,如果您选择选项(推荐), clisp会自动添加到您的 PATH 变量中。这意味着您只需打开一个新的命令提示符窗口并输入“clisp”即可启动编译器。

要运行 *.lisp 或 *.lsp 文件,只需使用 -

clisp hello.lisp

LISP - 程序结构

LISP 表达式称为符号表达式或 s 表达式。s 表达式由三个有效对象、Atomics、列表和字符串组成。

任何 s 表达式都是有效的程序。

LISP 程序可以在解释器上运行,也可以作为编译代码运行。

解释器在重复循环中检查源代码,这也称为读取-评估-打印循环(REPL)。它读取程序代码,对其进行评估,然后打印程序返回的值。

一个简单的程序

让我们编写一个 s 表达式来求三个数字 7、9 和 11 的和。为此,我们可以在解释器提示符下键入。

(+ 7 9 11)

LISP 返回结果 -

27

如果您想运行与编译代码相同的程序,请创建一个名为 myprog.lisp 的 LISP 源代码文件,并在其中键入以下代码。

(write (+ 7 9 11))

当您单击“执行”按钮或键入 Ctrl+E 时,LISP 会立即执行它,返回的结果是 -

27

LISP 使用前缀表示法

您可能已经注意到,LISP 使用前缀表示法。

在上面的程序中,+ 符号充当数字求和过程的函数名称。

在前缀表示法中,运算符写在操作数之前。例如,表达式,

a * ( b + c ) / d

将写为 -

(/ (* a (+ b c) ) d)

让我们再举一个例子,让我们编写代码将 60 华氏度的华氏温度转换摄氏度 -

此转换的数学表达式为 -

(60 * 9 / 5) + 32

创建一个名为 main.lisp 的源代码文件,并在其中键入以下代码。

(write(+ (* (/ 9 5) 60) 32))

当您单击“执行”按钮或键入 Ctrl+E 时,LISP 会立即执行它,返回的结果是:

140

LISP 程序的评估

LISP 程序的评估分为两部分 -

  • 通过阅读器程序将程序文本翻译成 Lisp 对象

  • 通过评估程序根据这些对象实现语言的语义

评估过程采取以下步骤 -

  • 阅读器将字符串转换为 LISP 对象或s 表达式。

  • 求值器定义了从 s 表达式构建的Lisp形式的语法。第二级求值定义了一种语法,用于确定哪些s 表达式是 LISP 形式。

  • 求值器作为一个函数,采用有效的 LISP 形式作为参数并返回一个值。这就是我们将 LISP 表达式放在括号中的原因,因为我们将整个表达式/形式作为参数发送给求值器。

“你好世界”计划

学习一门新的编程语言并不能真正成功,除非你学会了如何用该语言迎接整个世界,对吧!

因此,请创建名为 main.lisp 的新源代码文件并在其中键入以下代码。

(write-line "Hello World")

(write-line "I am at 'Tutorials Point'! Learning LISP")

当您单击“执行”按钮或键入 Ctrl+E 时,LISP 会立即执行它,返回的结果是 -

Hello World

I am at 'Tutorials Point'! Learning LISP

LISP - 基本语法

LISP 中的基本构建块

LISP 程序由三个基本构建块组成 -

  • Atomics
  • 列表
  • 细绳

Atomics是连续字符的数字或字符串。它包括数字和特殊字符。

以下是一些有效Atomics的示例 -

hello-from-tutorials-point
name
123008907
*hello*
Block#221
abc123

列表是一系列Atomics和/或括在括号中的其他列表

以下是一些有效列表的示例 -

( i am a list)
(a ( a b c) d e fgh)
(father tom ( susan bill joe))
(sun mon tue wed thur fri sat)
( )

字符串是用双引号括起来的一组字符。

以下是一些有效字符串的示例 -

" I am a string"
"a ba c d efg #$%^&!"
"Please enter the following details :"
"Hello from 'Tutorials Point'! "

添加评论

分号(;)用于指示注释行。

例如,

(write-line "Hello World") ; greet the world

; tell them your whereabouts

(write-line "I am at 'Tutorials Point'! Learning LISP")

当您单击“执行”按钮或键入 Ctrl+E 时,LISP 会立即执行它,返回的结果是 -

Hello World

I am at 'Tutorials Point'! Learning LISP

继续下一步之前的一些值得注意的点

以下是一些需要注意的要点 -

  • LISP 中的基本数字运算是 +、-、* 和 /

  • LISP 将函数调用 f(x) 表示为 (fx),例如 cos(45) 写为 cos 45

  • LISP 表达式不区分大小写,cos 45 或 COS 45 相同。

  • LISP 尝试评估所有内容,包括函数的参数。只有三种类型的元素是常量并且总是返回它们自己的值

    • 数字

    • 字母t,代表逻辑真。

    • nil,代表逻辑 false,以及空列表。

关于 LISP 表单的更多信息

在上一章中,我们提到LISP代码的求值过程分为以下步骤。

  • 阅读器将字符串转换为 LISP 对象或s 表达式。

  • 求值器定义了从 s 表达式构建的Lisp形式的语法。第二级求值定义了一种语法,用于确定哪些 s 表达式是 LISP 形式。

现在,LISP 表单就可以了。

  • 一个Atomics
  • 空列表或非列表
  • 任何以符号作为第一个元素的列表

求值器作为一个函数,采用有效的 LISP 形式作为参数并返回一个值。这就是我们将 LISP 表达式放在括号中的原因因为我们将整个表达式/形式作为参数发送给求值器。

LISP 中的命名约定

名称或符号可以包含除空格、左括号和右括号、双引号和单引号、反斜杠、逗号、冒号、分号和竖线之外的任意数量的字母数字字符。要在名称中使用这些字符,您需要使用转义字符 (\)。

名称可以有数字,但不能完全由数字组成,因为这样它就会被读作数字。同样,名称可以包含句点,但不能完全由句点组成。

单引号的使用

LISP 计算所有内容,包括函数参数和列表成员。

有时,我们需要按字面意思理解Atomics或列表,并且不希望将它们计算或视为函数调用。

为此,我们需要在Atomics或列表前面加上单引号。

以下示例演示了这一点。

创建一个名为 main.lisp 的文件并在其中键入以下代码。

(write-line "single quote used, it inhibits evaluation")
(write '(* 2 3))
(write-line " ")
(write-line "single quote not used, so expression evaluated")
(write (* 2 3))

当您单击“执行”按钮或键入 Ctrl+E 时,LISP 会立即执行它,返回的结果是 -

single quote used, it inhibits evaluation
(* 2 3) 
single quote not used, so expression evaluated
6

LISP - 数据类型

在 LISP 中,变量没有类型化,但数据对象有类型化。

LISP 数据类型可分为以下几类。

  • 标量类型- 例如数字类型、字符、符号等。

  • 数据结构- 例如列表、向量、位向量和字符串。

任何变量都可以将任何 LISP 对象作为其值,除非您已显式声明它。

虽然没有必要为 LISP 变量指定数据类型,但是,它有助于某些循环扩展、方法声明以及我们将在后面的章节中讨论的其他一些情况。

数据类型按层次结构排列。数据类型是一组 LISP 对象,并且许多对象可能属于一个这样的集合。

typep谓词用于查找对象是否属于特定类型。

type -of函数返回给定对象的数据类型。

LISP 中的类型说明符

类型说明符是系统定义的数据类型符号。

大批 固定数 包裹 简单字符串
Atomics 漂浮 路径名 简单向量
比格努姆 功能 随机状态 单浮点
少量 哈希表 比率 标准字符
位向量 整数 合理的 溪流
特点 关键词 可读表 细绳
[常见的] 列表 顺序 [字符串字符]
编译函数 长浮动 短浮动 象征
复杂的 有符号字节 t
缺点 无效的 简单数组 无符号字节
双浮点 数字 简单位向量 向量

除了这些系统定义的类型之外,您还可以创建自己的数据类型。当使用defstruct函数定义结构类型时,结构类型的名称将成为有效的类型符号。

实施例1

创建名为 main.lisp 的新源代码文件并在其中键入以下代码。

(setq x 10)
(setq y 34.567)
(setq ch nil)
(setq n 123.78)
(setq bg 11.0e+4)
(setq r 124/2)

(print x)
(print y)
(print n)
(print ch)
(print bg)
(print r)

当您单击“执行”按钮或键入 Ctrl+E 时,LISP 会立即执行它,返回的结果是 -

10 
34.567 
123.78 
NIL 
110000.0 
62

实施例2

接下来让我们检查一下上一个示例中使用的变量的类型。创建名为 main.c 的新源代码文件。lisp 并在其中键入以下代码。

(defvar x 10)
(defvar y 34.567)
(defvar ch nil)
(defvar n 123.78)
(defvar bg 11.0e+4)
(defvar r 124/2)

(print (type-of x))
(print (type-of y))
(print (type-of n))
(print (type-of ch))
(print (type-of bg))
(print (type-of r))

当您单击“执行”按钮或键入 Ctrl+E 时,LISP 会立即执行它,返回的结果是 -

(INTEGER 0 281474976710655) 
SINGLE-FLOAT 
SINGLE-FLOAT 
NULL 
SINGLE-FLOAT 
(INTEGER 0 281474976710655)

LISP - 宏

宏允许您扩展标准 LISP 的语法。

从技术上讲,宏是一个函数,它接受 s 表达式作为参数并返回 LISP 形式,然后对其进行求值。

定义宏

在 LISP 中,命名宏是使用另一个名为defmacro 的宏来定义的。定义宏的语法是 -

(defmacro macro-name (parameter-list))
"Optional documentation string."
body-form

宏定义由宏名称、参数列表、可选文档字符串以及定义宏要执行的作业的 Lisp 表达式体组成。

例子

让我们编写一个名为 setTo10 的简单宏,它将接受一个数字并将其值设置为 10。

创建名为 main.lisp 的新源代码文件并在其中键入以下代码。

(defmacro setTo10(num)
(setq num 10)(print num))
(setq x 25)
(print x)
(setTo10 x)

当您单击“执行”按钮或键入 Ctrl+E 时,LISP 会立即执行它,返回的结果是 -

25
10

LISP - 变量

在 LISP 中,每个变量都由一个符号表示。变量的名称是符号的名称,它存储在符号的存储单元中。

全局变量

全局变量在整个 LISP 系统中具有永久值,并且在指定新值之前一直有效。

全局变量通常使用defvar构造来声明。

例如

(defvar x 234)
(write x)

当你点击执行按钮,或者输入Ctrl+E,LISP立即执行它,返回的结果是

234

由于 LISP 中没有变量的类型声明,因此您可以直接使用setq构造为符号指定值。

例如

->(setq x 10)

上面的表达式将值 10 赋给变量 x。您可以使用符号本身作为表达式来引用变量。

符号值函数允许您提取存储在符号存储位置的值。

例如

创建名为 main.lisp 的新源代码文件并在其中键入以下代码。

(setq x 10)
(setq y 20)
(format t "x = ~2d y = ~2d ~%" x y)

(setq x 100)
(setq y 200)
(format t "x = ~2d y = ~2d" x y)

当您单击“执行”按钮,或键入 Ctrl+E,LISP 立即执行它并返回结果为。

x = 10 y = 20 
x = 100 y = 200

局部变量

局部变量是在给定过程中定义的。在函数定义中命名为实参的形参也是局部变量。局部变量只能在各自的函数内访问。

与全局变量一样,局部变量也可以使用setq构造来创建。

还有另外两个构造 - letprog用于创建局部变量。

let 构造具有以下语法。

(let ((var1  val1) (var2  val2).. (varn  valn))<s-expressions>)

其中 var1、var2、..varn 是变量名称,val1、val2、..valn 是分配给各个变量的初始值。

当执行let时,每个变量都会被赋予各自的值,最后对s 表达式进行求值。返回最后计算的表达式的值。

如果不包含变量的初始值,则会将其指定为nil。

例子

创建名为 main.lisp 的新源代码文件并在其中键入以下代码。

(let ((x 'a) (y 'b)(z 'c))
(format t "x = ~a y = ~a z = ~a" x y z))

当您单击“执行”按钮,或键入 Ctrl+E,LISP 立即执行它并返回结果为。

x = A y = B z = C

prog构造还将局部变量列表作为其第一个参数,后面是prog的主体和任意数量的 s 表达式。

prog函数按顺序执行 s 表达式列表并返回 nil,除非遇到名为return 的函数调用。然后计算并返回返回函数的参数。

例子

创建名为 main.lisp 的新源代码文件并在其中键入以下代码。

(prog ((x '(a b c))(y '(1 2 3))(z '(p q 10)))
(format t "x = ~a y = ~a z = ~a" x y z))

当您单击“执行”按钮,或键入 Ctrl+E,LISP 立即执行它并返回结果为。

x = (A B C) y = (1 2 3) z = (P Q 10)

LISP - 常量

在 LISP 中,常量是在程序执行期间永远不会改变其值的变量。常量是使用defconstant构造声明的。

例子

以下示例显示声明一个全局常量 PI,然后在名为area-circle 的函数中使用该值来计算圆的面积。

defun结构用于定义函数,我们将在函数章节中研究它。

创建一个名为 main.lisp 的新源代码文件,并在其中键入以下代码。

(defconstant PI 3.141592)
(defun area-circle(rad)
   (terpri)
   (format t "Radius: ~5f" rad)
   (format t "~%Area: ~10f" (* PI rad rad)))
(area-circle 10)

当您单击“执行”按钮,或键入 Ctrl+E,LISP 立即执行它并返回结果为。

Radius:  10.0
Area:   314.1592

LISP - 运算符

运算符是告诉编译器执行特定数学或逻辑操作的符号。LISP 允许对数据进行多种操作,并由各种函数、宏和其他结构支持。

允许对数据进行的操作可分为 -

  • 算术运算
  • 比较运算
  • 逻辑运算
  • 按位运算

算术运算

下表列出了 LISP 支持的所有算术运算符。假设变量A为 10,变量B为 20,则 -

显示示例

操作员 描述 例子
+ 添加两个操作数 (+AB) 将给出 30
- 从第一个操作数中减去第二个操作数 (- AB) 将给出 -10
* 将两个操作数相乘 (* AB) 将给予 200
/ 分子除以分子 (/BA) 将给出 2
模组,雷姆 模数运算符和整数除法后的余数 (mod BA) 将给出 0
因CF 增量运算符将整数值增加指定的第二个参数 (incf A 3) 将给出 13
十倍频 递减运算符将整数值减少指定的第二个参数 (decf A 4) 将给出 9

比较运算

下表显示了 LISP 支持的所有用于在数字之间进行比较的关系运算符。然而,与其他语言中的关系运算符不同,LISP 比较运算符可能需要两个以上的操作数,并且它们仅适用于数字。

假设变量A为 10,变量B为 20,则 -

显示示例

操作员 描述 例子
= 检查操作数的值是否全部相等,如果相等则条件成立。 (= AB) 不正确。
/= 检查操作数的值是否全部不同,如果值不相等则条件成立。 (/= AB) 为真。
> 检查操作数的值是否单调递减。 (> AB) 不正确。
< 检查操作数的值是否单调递增。 (<AB) 为真。
>= 检查任何左操作数的值是否大于或等于下一个右操作数的值,如果是,则条件为真。 (>= AB) 不正确。
<= 检查任何左操作数的值是否小于或等于其右操作数的值,如果是,则条件为真。 (<= AB) 为真。
最大限度 它比较两个或多个参数并返回最大值。 (最大 AB)返回 20
分钟 它比较两个或多个参数并返回最小值。 (最小 AB)返回 10

布尔值的逻辑运算

Common LISP 提供了三种逻辑运算符:and、ornot,它们对布尔值进行运算。假设A的值为 nil,B的值为 5,则 -

显示示例

操作员 描述 例子
它需要任意数量的参数。参数从左到右计算。如果所有参数的计算结果均为非零,则返回最后一个参数的值。否则返回 nil。 (和 AB)将返回 NIL。
或者 它需要任意数量的参数。参数从左到右计算,直到计算结果为非 nil,在这种情况下返回参数值,否则返回nil (或 AB)将返回 5。
不是 它接受一个参数,如果该参数的计算结果为nil ,则返回t (不是 A)将返回 T。

数字的按位运算

位运算符作用于位并执行逐位运算。按位与、或和异或运算的真值表如下 -

显示示例

p q p 和 q p 或 q 异或q
0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 0 1 1
Assume if A = 60; and B = 13; now in binary format they will be as follows:
A = 0011 1100
B = 0000 1101
-----------------
A and B = 0000 1100
A or B = 0011 1101
A xor B = 0011 0001
not A  = 1100 0011

LISP 支持的按位运算符如下表所示。假设变量A为 60,变量B为 13,则 -

操作员 描述 例子
洛甘德 这将返回其参数的按位逻辑与。如果未给出参数,则结果为 -1,这是此操作的标识。 (logand ab)) 将给出 12
洛焦尔 这将返回其参数的按位逻辑“或”。如果未给出参数,则结果为零,这是此操作的恒等式。 (logior ab) 将给出 61
对数异或 这将返回其参数的按位逻辑异或。如果未给出参数,则结果为零,这是此操作的恒等式。 (logxor ab) 将给出 49
洛格诺 这将返回其参数的按位 NOT。如果未给出参数,则结果为 -1,这是此操作的标识。 (lognor ab) 将给出 -62,
对数 这将返回其参数的按位逻辑等价(也称为异或非)。如果未给出参数,则结果为 -1,这是此操作的标识。 (logeqv ab) 将给出 -50

LISP - 决策

决策结构要求程序员指定一个或多个要由程序评估或测试的条件,以及如果确定条件为真则要执行的一个或多个语句,以及可选地如果条件确定则要执行的其他语句确定为假。

以下是大多数编程语言中典型决策结构的一般形式 -

决策

LISP 提供以下类型的决策结构。单击以下链接查看其详细信息。

先生。 构造与描述
1 条件

该构造用于检查多个测试操作子句。它可以与其他编程语言中的嵌套 if 语句进行比较。

2 如果

if 结构有多种形式。在最简单的形式中,它后面跟着一个测试子句、一个测试操作和一些其他后续操作。如果测试子句评估为 true,则执行测试操作,否则评估后续子句。

3 什么时候

在最简单的形式中,它后面跟着一个测试子句和一个测试操作。如果测试子句评估为 true,则执行测试操作,否则评估后续子句。

4 案件

此构造实现多个测试操作子句,如 cond 构造。然而,它评估一个关键形式并允许基于该关键形式的评估的多个操作子句。

LISP - 循环

可能存在一种情况,您需要多次执行一段代码。循环语句允许我们多次执行一条语句或一组语句,以下是大多数编程语言中循环语句的一般形式。

循环

LISP 提供以下类型的构造来处理循环要求。单击以下链接查看其详细信息。

先生。 构造与描述
1 环形

循环结构是 LISP 提供最简单的迭代形式。最简单的形式是,它允许您重复执行某些语句,直到找到return语句。

2 循环

循环 for 构造允许您实现类似 for 循环的迭代,这在其他语言中最常见。

3

do 结构还用于使用 LISP 执行迭代。它提供了一种结构化的迭代形式。

4 多时

dotimes 构造允许循环一些固定的迭代次数。

5 多利斯特

dolist 构造允许迭代列表的每个元素。

优雅地退出区块

块和返回允许您在出现任何错误时从任何嵌套块正常退出。

block函数允许您创建一个由零个或多个语句组成的主体的命名块。语法是 -

(block block-name(
...
...
))

return -from函数采用块名称和可选(默认为 nil)返回值。

以下示例演示了这一点 -

例子

创建一个名为 main.lisp 的新源代码文件并在其中键入以下代码 -

(defun demo-function (flag)
   (print 'entering-outer-block)
   
   (block outer-block
      (print 'entering-inner-block)
      (print (block inner-block

         (if flag
            (return-from outer-block 3)
            (return-from inner-block 5)
         )

         (print 'This-wil--not-be-printed))
      )

      (print 'left-inner-block)
      (print 'leaving-outer-block)
   t)
)
(demo-function t)
(terpri)
(demo-function nil)

当您单击“执行”按钮或键入 Ctrl+E 时,LISP 会立即执行它,返回的结果是 -

ENTERING-OUTER-BLOCK 
ENTERING-INNER-BLOCK 

ENTERING-OUTER-BLOCK 
ENTERING-INNER-BLOCK 
5 
LEFT-INNER-BLOCK 
LEAVING-OUTER-BLOCK

LISP - 函数

函数是一组共同执行任务的语句。

您可以将代码划分为单独的函数。如何在不同的函数之间划分代码取决于您,但逻辑上的划分通常是让每个函数执行特定的任务。

在 LISP 中定义函数

名为defun 的宏用于定义函数。defun宏需要三个参数-

  • 函数名称
  • 函数参数
  • 函数体

defun 的语法是 -

(defun name (parameter-list) "Optional documentation string." body)

让我们用简单的例子来说明这个概念。

实施例1

让我们编写一个名为averagenum的函数,它将打印四个数字的平均值。我们将把这些数字作为参数发送。

创建一个名为 main.lisp 的新源代码文件,并在其中键入以下代码。

(defun averagenum (n1 n2 n3 n4)
   (/ ( + n1 n2 n3 n4) 4)
)
(write(averagenum 10 20 30 40))

当您执行代码时,它会返回以下结果 -

25

实施例2

让我们定义并调用一个函数,当以圆的半径作为参数给出时,该函数将计算圆的面积。

创建一个名为 main.lisp 的新源代码文件,并在其中键入以下代码。

(defun area-circle(rad)
   "Calculates area of a circle with given radius"
   (terpri)
   (format t "Radius: ~5f" rad)
   (format t "~%Area: ~10f" (* 3.141592 rad rad))
)
(area-circle 10)

当您执行代码时,它会返回以下结果 -

Radius:  10.0
Area:   314.1592

请注意 -

  • 您可以提供一个空列表作为参数,这意味着该函数不带参数,列表为空,写为 ()。

  • LISP 还允许可选参数、多个参数和关键字参数。

  • 文档字符串描述了该函数的用途。它与函数的名称相关联,可以使用文档函数获得。

  • 函数体可以由任意数量的 Lisp 表达式组成。

  • 主体中最后一个表达式的值作为函数的值返回。

  • 您还可以使用return-from特殊运算符从函数返回值。

让我们简单讨论一下上述概念。单击以下链接查找详细信息 -

LISP - 谓词

谓词是测试其参数是否满足某些特定条件的函数,如果条件为假,则返回 nil,或者如果条件为真,则返回一些非 nil 值。

下表显示了一些最常用的谓词 -

先生。 谓词和描述
1

Atomics

它接受一个参数,如果参数是Atomics则返回 t,否则返回 nil。

2

平等的

它接受两个参数,如果它们结构相等则返回t ,否则返回nil

3

情商

它接受两个参数,如果它们是相同的相同对象,共享相同的内存位置,则返回t ,否则返回 nil

4

情商

它接受两个参数,如果参数是eq,或者它们是具有相同值的相同类型的数字,或者它们是表示相同字符的字符对象,则返回 t ,否则返回nil

5

埃文普

它接受一个数字参数,如果参数为偶数则返回t,否则返回nil 。

6

奇数

它接受一个数字参数,如果参数为奇数则返回t,否则返回nil 。

7

泽罗普

它接受一个数字参数,如果参数为零则返回t,否则返回nil 。

8

无效的

它接受一个参数,如果该参数的计算结果为 nil,则返回t,否则返回nil

9

列表

它接受一个参数,如果该参数计算结果为列表,则返回t ,否则返回nil

10

更大的

它接受一个或多个参数,如果只有一个参数或者参数从左到右依次变大,则返回t ,否则返回 nil 。

11

它接受一个或多个参数,如果只有一个参数或者参数从左到右依次变小,则返回t ,否则返回 nil 。

12

号码p

它接受一个参数,如果参数是数字则返回t ,否则返回nil 。

13

符号p

它接受一个参数,如果参数是符号则返回t ,否则返回nil

14

整数p

它接受一个参数,如果参数是整数,则返回t ,否则返回nil

15

理性

它接受一个参数,如果参数是有理数(比率或数字),则返回t ,否则返回nil

16

浮点

它接受一个参数,如果参数是浮点数,则返回t ,否则返回nil

17 号

雷尔普

它接受一个参数,如果参数是实数则返回t ,否则返回nil

18

复合体

它接受一个参数,如果参数是复数则返回t ,否则返回nil。

19

字符p

它接受一个参数,如果参数是字符则返回t ,否则返回nil

20

字符串p

它接受一个参数,如果参数是字符串对象,则返回t ,否则返回nil

21

阵列p

它接受一个参数,如果参数是一个数组对象,则返回t ,否则返回nil

22

包装

它接受一个参数,如果参数是一个包,则返回t ,否则返回nil。

实施例1

创建一个名为 main.lisp 的新源代码文件,并在其中键入以下代码。

(write (atom 'abcd))
(terpri)
(write (equal 'a 'b))
(terpri)
(write (evenp 10))
(terpri)
(write (evenp 7 ))
(terpri)
(write (oddp 7 ))
(terpri)
(write (zerop 0.0000000001))
(terpri)
(write (eq 3 3.0 ))
(terpri)
(write (equal 3 3.0 ))
(terpri)
(write (null nil ))

当您执行代码时,它会返回以下结果 -

T
NIL
T
NIL
T
NIL
NIL
NIL
T

实施例2

创建一个名为 main.lisp 的新源代码文件,并在其中键入以下代码。

(defun factorial (num)
   (cond ((zerop num) 1)
      (t ( * num (factorial (- num 1))))
   )
)
(setq n 6)
(format t "~% Factorial ~d is: ~d" n (factorial n))

当您执行代码时,它会返回以下结果 -

Factorial 6 is: 720

LISP - 数字

Common Lisp 定义了几种数字。数字数据类型包括LISP支持的各种数字。

LISP 支持的数字类型是 -

  • 整数
  • 比率
  • 浮点数字
  • 复数

下图显示了 LISP 中可用的数字层次结构和各种数字数据类型 -

数字类型

LISP 中的各种数字类型

下表描述了 LISP 中可用的各种数字类型数据 -

先生。 数据类型和描述
1

固定数

此数据类型表示不太大的整数,大部分在 -215 到 215-1 范围内(与机器相关)

2

比格努姆

这些是非常大的数字,其大小受到为 LISP 分配的内存量的限制,它们不是固定数字。

3

比率

以分子/分母形式表示两个数字的比率。当 / 函数的参数为​​整数时,它始终以比率形式生成结果。

4

漂浮

它代表非整数。有四种精度不断增加的浮点数据类型。

5

复杂的

它代表复数,用#c 表示。实部和虚部可以是有理数或浮点数。

例子

创建一个名为 main.lisp 的新源代码文件,并在其中键入以下代码。

(write (/ 1 2))
(terpri)
(write ( + (/ 1 2) (/ 3 4)))
(terpri)
(write ( + #c( 1 2) #c( 3 -4)))

当您执行代码时,它会返回以下结果 -

1/2
5/4
#C(4 -2)

数字函数

下表描述了一些常用的数字函数 -

先生。 功能说明
1

+、-、*、/

各自的算术运算

2

sin、cos、tan、acos、asin、atan

各自的三角函数。

3

辛、科什、tanh、阿科什、阿辛、阿坦

各自的双曲函数。

4

经验值

幂函数。计算 e x

5

出口

幂函数,同时取底数和幂。

6

开方

它计算数字的平方根。

7

日志

对数函数。给定一个参数,则计算其自然对数,否则以第二个参数为底。

8

共轭

它计算数字的复共轭。如果是实数,则返回数字本身。

9

腹肌

它返回数字的绝对值(或大小)。

10

最大CD

它计算给定数字的最大公约数。

11

液晶模组

它计算给定数字的最小公倍数。

12

伊索特

它给出小于或等于给定自然数的精确平方根的最大整数。

13

地板、天花板、截头、圆形

所有这些函数都将两个参数作为数字并返回商;float返回不大于ratio的最大整数,ceiling选择大于ratio的较小整数,truncate选择与ratio同号且最大绝对值小于ratio绝对值的整数,round选择最接近比率的整数。

14

fFloor、fceiling、ftruncate、fround

与上面的操作相同,但以浮点数形式返回商。

15

模组、雷姆

返回除法运算的余数。

16

漂浮

将实数转换为浮点数。

17 号

合理化,合理化

将实数转换为有理数。

18

分子、分母

返回有理数的各个部分。

19

真实部分、图像部分

返回复数的实部和虚部。

例子

创建一个名为 main.lisp 的新源代码文件,并在其中键入以下代码。

(write (/ 45 78))
(terpri)
(write (floor 45 78))
(terpri)
(write (/ 3456 75))
(terpri)
(write (floor 3456 75))
(terpri)
(write (ceiling 3456 75))
(terpri)
(write (truncate 3456 75))
(terpri)
(write (round 3456 75))
(terpri)
(write (ffloor 3456 75))
(terpri)
(write (fceiling 3456 75))
(terpri)
(write (ftruncate 3456 75))
(terpri)
(write (fround 3456 75))
(terpri)
(write (mod 3456 75))
(terpri)
(setq c (complex 6 7))
(write c)
(terpri)
(write (complex 5 -9))
(terpri)
(write (realpart c))
(terpri)
(write (imagpart c))

当您执行代码时,它会返回以下结果 -

15/26
0
1152/25
46
47
46
46
46.0
47.0
46.0
46.0
6
#C(6 7)
#C(5 -9)
6
7

LISP - 字符

在 LISP 中,字符被表示为字符类型的数据对象

您可以在字符本身之前的 #\ 之前表示字符对象。例如#\a表示字符a。

空格和其他特殊字符可以在字符名称前加上 #\ 来表示。例如,#\SPACE 表示空格字符。

以下示例演示了这一点 -

例子

创建一个名为 main.lisp 的新源代码文件,并在其中键入以下代码。

(write 'a)
(terpri)
(write #\a)
(terpri)
(write-char #\a)
(terpri)
(write-char 'a)

当您执行代码时,它会返回以下结果 -

A
#\a
a
*** - WRITE-CHAR: argument A is not a character

特殊字符

Common LISP 允许在代码中使用以下特殊字符。他们被称为半标准字符。

  • #\退格键
  • #\标签
  • #\换行
  • #\页
  • #\返回
  • #\抹去

字符比较函数

数字比较函数和运算符(例如 < 和 >)不适用于字符。Common LISP 提供了另外两组函数来比较代码中的字符。

一组区分大小写,另一组不区分大小写。

下表提供了功能 -

区分大小写的函数 不区分大小写的函数 描述
字符= 字符等于 检查操作数的值是否全部相等,如果相等则条件成立。
字符/= 字符不等于 检查操作数的值是否全部不同,如果值不相等则条件成立。
字符< 无字符型 检查操作数的值是否单调递减。
字符> char-greaterp 检查操作数的值是否单调递增。
字符<= 字符不大于 检查任何左操作数的值是否大于或等于下一个右操作数的值,如果是,则条件为真。
字符>= 字符非lessp 检查任何左操作数的值是否小于或等于其右操作数的值,如果是,则条件为真。

例子

创建一个名为 main.lisp 的新源代码文件,并在其中键入以下代码。

; case-sensitive comparison
(write (char= #\a #\b))
(terpri)
(write (char= #\a #\a))
(terpri)
(write (char= #\a #\A))
(terpri)
   
;case-insensitive comparision
(write (char-equal #\a #\A))
(terpri)
(write (char-equal #\a #\b))
(terpri)
(write (char-lessp #\a #\b #\c))
(terpri)
(write (char-greaterp #\a #\b #\c))

当您执行代码时,它会返回以下结果 -

NIL
T
NIL
T
NIL
T
NIL

LISP - 数组

LISP 允许您使用make-array函数定义一维或多维数组。数组可以存储任何 LISP 对象作为其元素。

所有数组都由连续的内存位置组成。最低地址对应于第一个元素,最高地址对应于最后一个元素。

秩

数组的维数称为其秩。

在 LISP 中,数组元素由非负整数索引序列指定。序列的长度必须等于数组的秩。索引从零开始。

例如,要创建一个包含 10 个单元格的数组,名为 my-array,我们可以编写 -

(setf my-array (make-array '(10)))

aref 函数允许访问单元格的内容。它有两个参数:数组的名称和索引值。

例如,要访问第十个单元格的内容,我们编写 -

(aref my-array 9)

实施例1

创建一个名为 main.lisp 的新源代码文件,并在其中键入以下代码。

(write (setf my-array (make-array '(10))))
(terpri)
(setf (aref my-array 0) 25)
(setf (aref my-array 1) 23)
(setf (aref my-array 2) 45)
(setf (aref my-array 3) 10)
(setf (aref my-array 4) 20)
(setf (aref my-array 5) 17)
(setf (aref my-array 6) 25)
(setf (aref my-array 7) 19)
(setf (aref my-array 8) 67)
(setf (aref my-array 9) 30)
(write my-array)

当您执行代码时,它会返回以下结果 -

#(NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL)
#(25 23 45 10 20 17 25 19 67 30)

实施例2

让我们创建一个 3×3 数组。

创建一个名为 main.lisp 的新源代码文件,并在其中键入以下代码。

(setf x (make-array '(3 3) 
   :initial-contents '((0 1 2 ) (3 4 5) (6 7 8)))
)
(write x)

当您执行代码时,它会返回以下结果 -

#2A((0 1 2) (3 4 5) (6 7 8))

实施例3

创建一个名为 main.lisp 的新源代码文件,并在其中键入以下代码。

(setq a (make-array '(4 3)))
(dotimes (i 4)
   (dotimes (j 3)
      (setf (aref a i j) (list i 'x j '= (* i j)))
   )
)
(dotimes (i 4)
   (dotimes (j 3)
      (print (aref a i j))
   )
)

当您执行代码时,它会返回以下结果 -

(0 X 0 = 0) 
(0 X 1 = 0) 
(0 X 2 = 0) 
(1 X 0 = 0) 
(1 X 1 = 1) 
(1 X 2 = 2) 
(2 X 0 = 0) 
(2 X 1 = 2) 
(2 X 2 = 4) 
(3 X 0 = 0) 
(3 X 1 = 3) 
(3 X 2 = 6)

make-array 函数的完整语法

make-array 函数还有许多其他参数。让我们看看这个函数的完整语法 -

make-array dimensions :element-type :initial-element :initial-contents :adjustable :fill-pointer  :displaced-to :displaced-index-offset

除了尺寸参数之外,所有其他参数都是关键字。下表提供了参数的简要说明。

先生。 论点和描述
1

方面

它给出了数组的维度。对于一维数组来说它是一个数字,对于多维数组来说它是一个列表。

2

:元素类型

是类型说明符,默认值为T,即任意类型

3

:初始元素

初始元素值。它将创建一个数组,其中所有元素都初始化为特定值。

4

:初始内容

作为对象的初始内容。

5

:可调节的

它有助于创建一个可调整大小(或可调整)的向量,其底层内存可以调整大小。参数是一个布尔值,指示数组是否可调整,默认值为 NIL。

6

:填充指针

它跟踪实际存储在可调整大小的向量中的元素数量。

7

: 位移至

它有助于创建与指定数组共享其内容的置换数组或共享数组。两个数组应该具有相同的元素类型。:displaced-to 选项不能与 :initial-element 或 :initial-contents 选项一起使用。该参数默认为零。

8

:位移索引偏移量

它给出了创建的共享数组的索引偏移量。

实施例4

创建一个名为 main.lisp 的新源代码文件,并在其中键入以下代码。

(setq myarray (make-array '(3 2 3) 
   :initial-contents 
   '(((a b c) (1 2 3)) 
      ((d e f) (4 5 6)) 
      ((g h i) (7 8 9)) 
   ))
) 
(setq array2 (make-array 4 :displaced-to myarray :displaced-index-offset 2)) 
(write myarray)
(terpri)
(write array2)

当您执行代码时,它会返回以下结果 -

#3A(((A B C) (1 2 3)) ((D E F) (4 5 6)) ((G H I) (7 8 9)))
#(C 1 2 3)

如果移位数组是二维的 -

(setq myarray (make-array '(3 2 3) 
   :initial-contents 
   '(((a b c) (1 2 3)) 
      ((d e f) (4 5 6)) 
      ((g h i) (7 8 9)) 
   ))
) 
(setq array2 (make-array '(3 2) :displaced-to myarray :displaced-index-offset 2)) 
(write myarray)
(terpri)
(write array2)

当您执行代码时,它会返回以下结果 -

#3A(((A B C) (1 2 3)) ((D E F) (4 5 6)) ((G H I) (7 8 9)))
#2A((C 1) (2 3) (D E