执行环境

( c o m m a n d ) (command) (command)

Lisp中,涉及到操作相关,都必须是一个列表,也就是,必须使用小括号()进行包裹。

大部分的交互式界面推出方式如下

enviormentcommand
shellexit
pythonexit()
lisp(exit)

所以,对于这个环境的理解可能更明确了一些。

一般顺序

( f u n c d a t a _ 1 d a t a _ 2 …   ) (func \quad data\_1 \quad data\_2 \quad \dots ) (funcdata_1data_2)

默认的执行顺序,是对于每一个元素进行求值,然后统一进行运算;可以看作是reduce的操作。

可以尝试一下如下操作

(+ 1 2)
(+ 1 2 3)
(exit)
(exit 0)
(list 1 2 3 4 5)

基本数据

数值类型

数值类型是最直接的类型,不用经过计算或者转换

2
3
4

字符类型

如果输入了其他字符,一般是不被接受的,因此需要告诉lisp不需要去计算,保留原样即可。

(quote a)

布尔类型

charactervalue
Ttrue
NILfalse

和数值一样,字面量可用。

列表

列表的话,算是最常用的类型了。

我们所需要的环境,也可以算作是一个列表。

一般使用list进行列表的定义

(lisp a b c d)

一些操作

不操作

(quote a)

quote的操作就是不操作,原样保留。但是特殊的是,它是单值操作,也就是操作元素只允许一个数据。

(list (quote (+ 1 2)) (+ 1 2))

可以好好体会一下

'q

如果是全称,也就是quote,需要环境(),但是,如果使用的是',直接就可以进行操作了。

列表

(list a b c)

把三个数据组成列表,但是还有一种直接定义的方式

(quote (a b c))

因为不进行计算的直接保留,所以还是原始的列表形式。

`(a b c)

列表操作

转化成列表是一种操作,同时,列表也是一种元素,可以被操作。

  • 获取第一个元素
(car '(a b c)); A
  • 获取第一个之后的元素
(cdr '(a b c)); (B C)

可以看到

  1. 字符会被显示为大写
  2. car获取的是单个元素,cdr返回的是包含剩余元素的列表
(car (cdr (cdr '(a b c d e f))))

通过这种方式,我们可以进行任意元素的获取

赋值

(let ((a 1)) (format t "first ~A~%" a) (format t "second ~A~%" a))
  • 关键字:let
  • 元组赋值:赋值操作在()中,但是每一对赋值表示为元组
  • 影响范围:顺序执行赋值之后的操作语句

判断

(if T 2 3); 2

if是一个三元素的操作,通过条件筛选元素。

(if (> 2 1) (+ 2 3) (- 10 3)); 5

逻辑

  • and
(and T NIL); NIL
  • or
(or T NIL); T
  • not
(not nil);T
(null nil);T

相等

(eql 'a 'a);T
(eql 1 1); T
(eql '(a b c) '(a b c)); NIL

只能进行数值/字面量比较

函数

(defun add (a b) (+ a b))
(add 1 4); 5

其中包含四个部分

  • 关键字:defun
  • 函数名:add
  • 参数表:(a b)
  • 函数体:(+ a b)

后续的操作都算作是函数体

  • 单操作函数体
(defun _max (a b) (if (> a b) a b))
(_max 4 6); 6
  • 多操作函数体
(defun add (a b) 
    (format t "before~%")
    (format t "~A + ~A = ~A~%" a b (+ a b))
    (format t "after~%")
)

递归

检查一个元素,是否存在于一个列表中

(defun contains (obj lst) (
    if (null lst) nil (
        if (eql obj (car lst)) T (contains obj (cdr lst))
    )
))

输出

(format t "~A + ~A = ~A~%" 1 2 (+ 1 2)); 1 + 2 = 3
  • 关键字:format
  • 操作符:t
  • 占位符:~A
  • 换行符:~%

输入

(read)

一直阻塞等待输入。

参数输入

(defun hello () 
    (format t "hello, wath's your name?~%")
    (let ((name (read)))
        (format t "斯巴拉西, we are both named ~A~%" name)
    )
)

定义赋值

一般赋值

前面的let可以进行赋值,但是值得注意的是,它引入的是局部变量。

(let ((p 1)));赋值
p;此时显示是报错的,因为外部访问不了局部变量

对于全局变量的定义和赋值,一般使用setf

(setf m 888);
m;外部是可以直接进行访问的

全局变量

(defparameter judas 99)
  • 全局变量重新定义,会覆盖原来的值
  • setf进行赋值,会更新原来的值

全局常量

(defconstant fuck 99)
  • 全局常量的重新定义,名字冲突会报错
  • 全局常量的复制操作,会报错

迭代

(defun godme (start end) (
    do ((i start (+ i 1)))
    ((> i end) `done)
    (format t "value : ~A~%" i)
))
  • 关键字:do

  • 初始化和更新:((i start (+ i 1))),其中传入的单个数据是一个三元组

    • 变量声明:i
    • 初始化:start
    • 更新操作:(+ i 1)
  • 结束条件:((> i end) `done),满足条件就会退出

  • 函数区域:剩下的可以跟随多个操作语句

(defun godme (start end) (
    do ((i start (+ i 1) ) (j end (- j 1)))
    ((eql i j) `done)
    (format t "i=~A  j=~A" i j)
    (format t " -end~%")
))

函数对象

函数对象

很多字符,都不能单纯的作为字面量进行使用,需要进行语义保留quote,除非是已经定义的变量。

那么,操作符号是什么。

经过测试,操作符号是可以进行quote的,也就是说,直接输入的时候,他们还代表着其他的含义。

单纯的输入+,会打印上一条命令

他们都是函数,每次都是直接进行的调用,他们的实体可以使用function进行查看。

(function +);#<SYSTEM-FUNCTION +>
(function eql);#<SYSTEM-FUNCTION EQL>

遗憾的是,下面这条命令是失败的

(function function)

quotefunction也有简写模式#'

#' eql
#' +

函数参数

apply

(apply #'+ '(1 2 3))
(apply #'+ 1 2 '(3))
  • 支持函数作为参数
  • 后续跟随多个参数
  • 最后一个参数必须是列表

python表示一下的话,就是这样了

def apply(func, *args):
  return func(*args)

funcall

(funcall #'+ 1 2 3)

apply相同,比较好的是,最后一个参数不强制要求为列表。

匿名函数

使用lambda而非defun,可以省略函数名。

((lambda (x) (+ x 99)) 1);100

实体和调用

(setf add (lambda (x y) (+ x y)))
(funcall add 1 2)

创建了一个函数的变量add,相较于其他变量,它的特殊点在于可以操作其他变量。

异同

(add 1 2);异常

有必要注意一下,此时获取的这个函数变量,我们居然无法直接进行调用。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐