Lisp 2021


Setup 设置

We can use SBCL which is a high-performance Common Lisp compiler. To set up our environment follow these steps based on your environment:

我们可以使用 SBCL,它是一个高性能的 Common Lisp 编译器。要设置我们的环境,请根据您的环境遵循以下步骤:

Ubuntu/Debian To install SBCL on either, just run:

Ubuntu/Debian 要安装 SBCL,只需运行:

$ sudo apt-get install sbcl

Arch Linux Since SBCL is available from the official repositories, you can install it with:

由于 SBCL 可以从官方软件库中获得,你可以通过以下方式安装它:

$ sudo pacman -S sbcl

OS X To install SBCL on OS X, just do:

要在 OS x 上安装 SBCL,只需要:

$ brew install sbcl

Now, once installed close and open a new terminal session and type


$ sbcl

It will open the REPL for Lisp

它将为 Lisp 打开 REPL

Repl for lisp
Repl for lisp 代替 lisp

Now just type (+ 1 2) and press enter. It should display 3.

现在只要输入(+ 12)然后按回车,它应该会显示3。

But, working on this REPL would be inefficient so we would rather use our favorite text editor to create lisp files ( extension is *.lisp) and run them.

但是,处理这个 REPL 效率很低,所以我们宁愿使用我们最喜欢的文本编辑器来创建 lisp 文件(扩展名是 *)。运行它们。

So create a lisp file test.lisp and enter this line (print "Hello World"). Now in our terminal run this:

因此,创建一个 lisp 文件 test.lisp 并输入这一行(打印“ Hello World”):

$ sbcl --script test.lisp

If you are VSCode user like me, install this extension to get Lisp syntax highlighting.

如果你和我一样是 VSCode 用户,安装这个扩展就可以获得 Lisp 语法突显。

Lisp Extension
Lisp Extension Lisp 扩展

This should display Hello World and now we are good to go.

这应该会显示 Hello World,现在我们可以开始了。

REPL stands for Read-Eval-Print-Loop, it is similar to python interpreter where it reads the input from the user, then evaluates it, prints the results and then again continues this in loop.

REPL 代表 Read-Eval-Print-Loop,它类似于 python 解释器,它从用户那里读取输入,然后计算,输出结果,然后再循环继续这个过程。

From within the environment provided by the REPL, you can define and redefine program elements such as variables, functions, classes, and methods; evaluate any Lisp expression; load files containing Lisp source code or compiled code; compile whole files or individual functions; enter the debugger; step through code; and inspect the state of individual Lisp objects.

在 REPL 提供的环境中,你可以定义和重新定义程序元素,比如变量、函数、类和方法; 计算任何 Lisp 表达式; 加载包含 Lisp 源代码或编译代码的文件; 编译整个文件或单个函数; 输入调试器; 逐步执行代码; 检查单个 Lisp 对象的状态。

Understanding (+ 1 2) 理解(+ 12)

Anything in parentheses is a list, in this case, a list of three elements, the symbol +, and the numbers 1 and 2. Lisp, in general, evaluates lists by treating the first element as the name of a function and the rest of the elements as expressions to be evaluated to yield the arguments to the function. In this case, the symbol + names a function that performs addition. 1 and 2 evaluate to themselves and are then passed to the addition function, which returns 3. The value 3 is passed to the printer, which prints it.

括号中的任何内容都是一个列表,在本例中是一个包含三个元素的列表,符号 + 和数字1和2。一般来说,Lisp 通过将第一个元素视为函数的名称来计算列表,将其余元素视为要计算的表达式,以便为函数生成参数。在这种情况下,符号 + 命名一个执行加法的函数。1和2计算到它们自己,然后被传递给加法函数,它返回3。值3被传递给打印机,打印机打印它。

Hence we get 3 as output.


Why so weird? 为什么这么奇怪?

The few things anyone from C++, Java, Python background will notice is that why are there so many parentheses, and why like whyyyyyyy? prefix notation.

有 c + + ,Java,Python 背景的人会注意到的少数事情是为什么有这么多括号,为什么喜欢 whyyyyy?前缀表示法。


I didn't find the answer, if you do let me know


Basic Syntax 基本语法

Now in Lisp, whatever is deliminated by parentheses and has space-separated values is called a list.

现在在 Lisp 中,任何用括号分隔并且有空格分隔的值都称为列表。

Then we have forms. A form is a list with a command function name at the beginning, like for example (+ 1 2) is a form because it has + which is command/function name and 1 and 2 are just params to be passed and hence the output will be 3.

然后我们就有了表格。表单是一个在开头有一个命令函数名的列表,例如(+ 12)是一个表单,因为它有 + ,这是命令/函数名,1和2只是要传递的参数,因此输出为3。

We can nest our forms inside another form, like this (+ 1 (+ 2 3))

我们可以将我们的形式嵌入到另一个形式中,比如这个(+ 1(+ 23))

Basically everything is a list inside of Lisp

基本上,Lisp 中的所有东西都是一个列表

Comments 评论

In Lisp comments start with ;.

在 Lisp 中,注释以; 开头。

Mutli-line comments in Lisp is given by #|| at the beginning and ||# at the end.

Lisp 中的 Mutli-line 注释由 # | 在开头,| | # 在结尾。



(+ 1 (+ 2 3)) ; nested forms (single comment)

Example of multi-line comments

<clipboard-copy aria-label="Copy" class="ClipboardButton btn js-clipboard-copy m-2 p-0 tooltipped-no-delay" data-copy-feedback="Copied!" data-tooltip-direction="w" value="(+ 1 (+ 2 3)) ; nested forms (single comment)

“ clipboard-Copy aria-label = “ Copy”class = “ clipboard button btn js-clipboard-Copy m-2 p-0 tooltipped-no-delay”data-Copy-feedback = “ Copy!”data-tooltip-direction = “ w”value =”(+ 1(+ 23)) ; 嵌套表单(single comment)

#|| Example of multi-line comments ||# " tabindex="0" role="button">

# | | | 多行注释 | | #”tabindex = “0”role = “ button”>

Numbers 数字

Fairly simple, any sequence of digits- possibly prefaced with a sign (+ or -), containing a decimal point (.) or a solidus (/), or ending with an exponent marker is read as a number.

相当简单,任何数字序列-可能前面有一个符号(+ 或 -) ,包含一个小数点(.)或者一个 solidus (/) ,或者以一个指数标记结束被读作一个数字。

Numbers examples


Strings 弦乐

They are enclosed in double-quotes. If we want to escape characters we use the \ (backslash). But which chars can we escape? The answer is only 2.


  1. Double quotes 双引号"
  2. Backslash itself \ All other characters can be included in a string literal without escaping, regardless of their meaning outside a string.

Strings examples


If you execute this script using the command


$ sbcl --script strings.lisp

The output will be something like this:


"She replied \"Yes\""
"This \\ is backslash"

There are additional quotes in the strings. We can get rid of them if we use the format command instead of the print.

字符串中有额外的引号。如果我们使用 format 命令而不是 print 命令,我们可以删除它们。

Tokens like the name of a command, variable, functions are represented by objects called symbols. One thing to keep in mind is to not using whitespace while naming these because, the elements of lists are separated by whitespace. Standard style, these days, is to write code in all lowercase and for separation use a dash -.


Two important constants that are defined this way are T and NIL, the canonical true and false values respectively.

以这种方式定义的两个重要常数是 t 和 NIL,它们分别是规范的真值和假值。

Format 格式

By default print or format doesn't print a new line for that we use ~% which is the equivalent of new line character.

默认情况下,print 或 format 不会为我们使用 ~% 的行打印新行,这相当于新行字符。

Format examples


The output will be:


She replied "Yes"
This \ is backslash

The format function takes two arguments

Format 函数接受两个参数

  1. Destination for its output. 输出的目的地
  2. Control string that contains literal text and embedded directives.
  3. There can be other arguments but they wouldn't be arguments to format function rather would interpolate values into the output. Known as format arguments.
**First Arg** * * 第一个 Arg * * **Means** * * 意味着 * *
`t` “ t‘ the output will be standard output i.e the console输出将是标准输出,即控制台
`nil` ‘ nil’ won't print rather return a string不会打印而是返回一个字符串

The second argument, the control string, is, in essence, a program in the FORMAT language. The directives that we pass to the control string may or may not need any arguments, like ~% just emits a newline character and doesn't consume any arguments.

第二个参数,即控制字符串,本质上是 FORMAT 语言中的一个程序。我们传递给控制字符串的指令可能需要也可能不需要任何参数,比如 ~% 只发出换行符,不使用任何参数。

All directives start with ~ (tilde).

所有指令都以 ~ (波浪)开头。


Represents new line.



~ a

The most general-purpose directive is ~A, which consumes one format argument of any type and outputs it in aesthetic (human-readable) form.

最通用的指令是 ~ a,它使用任何类型的一个格式参数,并以唯美(人类可读)的形式输出。

(format t "The value is: ~a ~%" 10)
(format t "The value is: ~a ~%" "foo")
(format t "The value is: ~a ~%" (list 1 2 3))

will display:


The value is: 10
The value is: foo
The value is: (1 2 3)



This is similar to ~a but shows quotes around the string value.

这与 ~ a 类似,但在字符串值周围显示引号。

(format t "The value is: ~s ~%" 10)
(format t "The value is: ~s ~%" "foo")
(format t "The value is: ~s ~%" (list 1 2 3))

will display


The value is: 10
The value is: "foo"
The value is: (1 2 3)

~d Five closely related directives format integer values: ~d, ~x, ~o, ~b, and ~r. The most frequently used is the ~d directive, which outputs integers in base 10.

~ d 五个密切相关的指令格式整数值: ~ d,~ x,~ o,~ b 和 ~ r。最常用的是 ~ d 指令,它输出以10为基数的整数。

If we put : like this ~:d it will print commas to format the number.

如果我们输入: like this ~ : d,它会打印逗号来格式化数字。

(format t "The value is: ~d ~%" 10000000)
(format t "The value is: ~:d ~%" 10000000)

will display


The value is: 10000000
The value is: 10,000,000

~f Used for floating point directive


(format t "The value of PI is: ~f ~%" pi)
(format t "The value of PI is: ~,4f ~%" pi)
(format t "The value of PI is: ~e ~%" pi)

will display


The value of PI is: 3.141592653589793
The value of PI is: 3.1416
The value of PI is: 3.141592653589793d+0

Function 功能

The most basic functionalities of Lisp are:

Lisp 最基本的功能包括:

  1. Functions 功能
  2. Variables 变量
  3. Macros

We are discussing the first one right now.


To define new functions we use the defun name. Usually function names contain only alphabetic characters and hyphens, but other characters are allowed and are used in certain naming conventions.

要定义新函数,我们使用 defun 名称。函数名通常只包含字母字符和连字符,但允许使用其他字符,并在某些命名约定中使用。

Basic function structure looks like this:


(defun function-name (parameter*)
  "Optional documentation string."

To call the function we write:


(function-name argument*)

Function examples


Look at this function:


(defun hello-word() (format t "Hello World ~%"))

Name of the function is hello-word, argument list is empty hence doesn't need additional parameters to call it.

函数的名称是 hello-word,参数列表是空的,因此不需要额外的参数来调用它。

Remember, the value of the last expression inside the function is the value that it returns.


Q. Write a function that takes two arguments and displays the sum also returns it


Ans. In the attached code examples linked above.


Variables 变量

It is the next building block. There are two types of variables:


  1. Lexical (Local) 词汇(本地)
  2. Dynamic (Global) 动态(全球)

As we all know variables are names places that holds value. Unlike, languages like C++, Java variables are not typed, i.e they can hold any type of values.

众所周知,变量是保存值的名称位置。不像 c + + 这样的语言,Java 变量不是类型化的,也就是说它们可以容纳任何类型的值。

Remember Lisp is a strongly typed language.

记住 Lisp 是一种强类型语言。

Each time a function is called, Lisp creates new bindings to hold the arguments passed by the function's caller. A binding is the runtime manifestation of a variable.

每次调用函数时,Lisp 都会创建新的绑定来保存函数调用者传递的参数。绑定是变量在运行时的表现形式。

We can use the let to create variables (JS vibes). Let takes a form as the first argument, in the form each item (atom in Lisp term) is a initialisation list in itself where the first item is the symbol and the second is the object to be initialised with. It also has a body and the entire let returns the last evaluated expression of the body.

我们可以使用 let 来创建变量(JS vibes)。Let 以一个表单作为第一个参数,在表单中,每个项(Lisp 术语中的 atom)本身是一个初始化列表,其中第一个项是符号,第二个是要初始化的对象。它还有一个 body,并且整个 let 返回 body 的最后一个计算表达式。

(let ((x 10) (y 20) z)

Variable examples


When the let form is evaluated, all the initial value forms are first evaluated. Then new bindings are created and initialized to the appropriate initial values before the body forms are executed. Within the body of the let, the variable names refer to the newly created bindings. After the let, the names refer to whatever, if anything, they referred to before the let.

在计算 let 表单时,首先计算所有初始值表单。然后,在执行主体窗体之前,创建新绑定并初始化为适当的初始值。在 let 的主体中,变量名引用新创建的绑定。在租赁之后,这些名称指的是租赁之前他们所指的任何东西。

The scope of function parameters and LET variables refers to the area of the program where the variable name can be used to refer to the variable's binding. It is delimited by the form that introduces the variable.

函数参数和 LET 变量的作用域指的是程序中可以使用变量名来引用变量的绑定的区域。它由引入变量的形式分隔。

If inital value no provided it stores NIL by default.

如果首字母值为 no,则默认情况下存储 NIL。

But if we want to specify global variables (dynamic variables) we use defvar.

但是如果我们想要指定全局变量(动态变量) ,我们使用 defvar。

Global variables are conventionally named with names that start and end with *

全局变量通常以 * 开头和结尾的名字命名

(defvar *myvar* 0
  "this is documentation line, 0 is the initial value here"

If we want to update or mutate a variable we use the setf. setf takes two parameter first is the variable whose value we want to set and second is the new value.

如果我们想要更新或者变异一个变量,我们使用 setf。Setf 接受两个参数,第一个是我们要设置其值的变量,第二个是新值。

Each time a function is called, Lisp creates new bindings to hold the arguments passed by the function's caller. Remember function parameter holds object referrences.Thus, you can assign a new value to a function parameter within the body of the function, and it will not affect the bindings created for another call to the same function. But, if the object passed to a function is mutable and you change it in the function, the changes will be visible to the caller since both the caller and the callee will be referencing the same object.

每次调用函数时,Lisp 都会创建新的绑定来保存函数调用者传递的参数。记住函数参数保存对象引用。因此,您可以为函数体内的函数参数分配一个新值,而且它不会影响为同一函数的另一个调用创建的绑定。但是,如果传递给函数的对象是可变的,并且您在函数中对其进行了更改,那么更改将对调用方可见,因为调用方和被调用方都将引用同一个对象。

Constants 常量

We can even declare constant variables which we cannot mutate in future. For that we use defconstant


Arithmetic Operations 算术运算

The basic operators are +, -, *, /

基本操作符是 + ,-,* ,/

To get the remainder we use the rem or mod command.

为了得到剩余部分,我们使用 rem 或 mod 命令。

We also have some other commands whose functionality is evident from the names. Eg. expt, sqrt, exp, log, floor, ceiling, max, min, oddp, evenp


For checking equality, we use the eq or = command. Similarly, we have the > < commands as well.

为了检查相等性,我们使用 eq 或 = 命令。

Arithmetic Examples


Conditional Operations 条件操作

The most basic conditional forms in Lisp is the if then, the structure of the form is like this:

Lisp 中最基本的条件形式是 if then,表单的结构如下:

(if condition then-form [else-form])

This forms returns the true value or the else value, if the else value is not provided it will simply return nil.

这个窗体返回 true 值或 else 值,如果没有提供 else 值,它将简单地返回 nil。

Let's quickly cover how to read data There is also a read function which takes input from a stream we specify. So if we call the read function, it will wait for our prompt and when we press enter it will return whatever we entered.

让我们快速介绍一下如何读取数据还有一个读取函数,它从我们指定的流中获取输入。因此,如果我们调用 read 函数,它将等待我们的提示,当我们按 enter 键时,它将返回我们输入的任何内容。

Q. WAP to ask the name and age and check if the person is above 18

问: WAP 询问这个人的姓名和年龄,并检查他是否在18岁以上

A. Conditional Examples

A. 有条件的例子