lisp(本文专指common lisp)语言简介

lisp程序员能够并且经常编写一些能够写程序的程序,对于程序生成程序的这种特性,

因为lisp是主流语言中唯一一个提供一些方便的抽象来让你完成这个任务的语言,所以

lisp是主流语言中唯一一个广泛运用这个特性的语言。

编程其实就是寻求编写最优美的程序  ---高德纳

lisp黑客精神可以用两句话概括:编程应该是有趣的,程序应该是优美的。

约翰麦卡锡和他的学生于1958年展开lisp的初次实现工作,lisp是继Fortran之后,仍在使用

的最古老的的程序语言,他仍然走在程序语言技术的最前面,懂lisp的程序员会告诉你,某种

东西使得lisp与众不同。

lisp与众不同的部分语言是它被设计成能够自己进化,当新的抽象概念风行时,这些新概念

在lisp中是最容易实现的,lisp就像DNA一样,永远不会过时。

程序语言教你不要做他没有提供的事情。因为可扩展的思想深植于lisp当中,使得lisp成为实现可扩展

软件的理想语言。

lisp是交互式的语言,任何系统都包含一个交互式的前端叫做顶层

数字对自身求值

CL-USER> 1

1

CL-USER>(+ 2 3)

5

在上述表达式中 + 是操作符  2 和 3 是参数

lisp使用的是波兰式 这是lisp最美好的东西

CL-USER>(+ 3 4 5)   就算有三个参数 也可以只使用一次 操作符 而

大多数编程语言 必须要写成这样 3 + 4 +5

前缀表达式的灵活性显著,lisp中 + 可以接受任意数目的参数,包括没有参数

CL-USER>(+)

0

因为操作符可以接收任意数目的参数,我们需要用括号来注明开始和结束

另一个lisp表示法美丽的地方是:它就是这么简单,所有lisp表达式要么是 1 这样的数

原子(atom),要么是包在括号中,由零个或者多个表达式组成的列表

不是原子就是列表   注意: NIL 即是原子也是列表

在lisp中我们使用单一的表示法来表达所有的概念。

lisp求值规则:

1.首先对参数从左到右求值

2.参数的值传入以操作符命名的函数

如果参数本身也是函数调用,也遵循上面的规则

不是所有的操作符都是函数,但大部分是,而函数调用都是这样子求值的

一个不遵循lisp求值规则的 操作符是 quote(有自己的求值规则)  什么都不做,原样返回

CL-USER> (quote (+ 2 3))

(+ 2 3)

为了方便起见 使用 ' 缩写

作用是作为一种保护表达式被求值的方式

数据

整数(integer)

字符串(string)    使用双引号包裹

符号(symbol)     符号不对自身求值 通常要用 ' 引用

CL-USER>'article

ARTICLE  通常他们被转换成大写

' 可以保护整个表达式不被求值

列表和表达式的关系

如果一个列表被引用,则求值规则对列表自身求值

如果没有被引用,则列表被视为代码

列表也要引用 不然会认为是函数调用

list 函数  实参会被求值

CL-USER>(list '(+ 2 3) (+ 2 3))

((+ 2 3) 5)

空列表  nil 或者 () 来表示  引用与否无所谓  因为 nil 也是对自身求值

列表操作

>>>(cons 'a '(b c d))

(A B C D)  如果传入的第二个参数是列表,则返回组成的新列表

list函数只是一个把几个元素加到 nil 上的快捷方式

(cons 'a (cons 'b nil))  ->(A B)

car 取出列表的第一个元素

cdr 取出剩余元素

third 取出列表的第三个元素快捷方式

真与假

符号 t 表示真的缺省值  和 nil 一样对自身求值

(listp '(a b c d))  当参数是一个列表  则 listp 返回真  此类函数称为判断式 通常以 p 结尾

在lisp中假用 nil 表示  虽然 T 是真的缺省值,但是任何非 nil 的东西 都被设为 真  0 也被视为 真

(null nil) ---T   如果参数是空表 则返回 真   其他的都返回假

(not nil) ---T  如果参数是 假 返回真       除了(not nil) 返回 T  其他的都返回假(NIL)

条件式 if 它通常接受三个参数  一个是test表达式 一个是 then 表达式 一个是else表达式

第三个可以没有 默认为 nil

if 不能使用函数来实现  因为函数的参数永远都会被求值

CL-USER>(if (listp 27) 3 4) ----> 4

and 和 or 逻辑操作符 和条件式类似  and 和 or 会求值直到 值可以确定下来  称之为 宏

和特殊操作符一样 宏可以绕过一般求值规则

(and t (+ 1 2)) -> 3

函数

(defun our-third(x)
    (car (cdr (cdr x))))

符号 就是变量的名字  它本身也是以对象的方式存在

符号被引用 就是为了避免被视为变量

可以把函数想成是 广义版的 lisp 表达式

(defun sum-greater(x y z)
       (> (+ x y) z))

lisp不对 程序、过程、函数来区别  函数做了所有的事

递归

member 函数测试某一个东西是否在 一个列表中        eql测试两个实参是否是同一对象

(defun our-member(obj lst)
(if (null lst)
nil
(if (eql obj (car lst))
lst
(our-member obj (cdr lst)))))

输入输出

format 接受 两个或者以上的参数  第一个是 表示他要被打印到 哪里 t 表示标准输出 也就是终端上

第二个 是模板字符  剩余的参数就是 要插入的内容

(format t "~A plus ~A is ~A~%" 2 3 (+ 2 3))

~% 表示一个换行

标准输入函数式 read

(defun askem(string)
(format t "~A" string)
(read))

read很强大 是一个完整的lisp解析器 会将读入的字符解析并且返回对应的lisp对象
如果是数字 就会解析成整数

函数主体可以有多个表达式  但是函数只会返回最后的一个表达式的值

副作用:表达式被求值后,对外部的状态做了某些改变  format 不仅返回值

还打印了一些东西,这就是副作用

当我们要写没有副作用对的程序时,则定义多个表达式的函数主体是没有意义的

最后的表达式之前的值都会被舍弃   如果没有用,为什么要对他进行求值呢?

变量

let 引入局部变量

(let ((x )(y ))   x y只在let里面有效
(+ x y)) 一组变量之后是一个有表达式的函数体
(defun ask-int?()
(format t "how old are you?")
(let ((a (read)))
(if (numberp a)
a
(ask-int?)))) 判断输入的是否是整数

numberp 是一个判断式 判断是否为数字

(numberp 2) -> T (numberp "2") -> NIL

全局变量

全局变量 (defparameter *glob* 99)  在任何地方都可以存取  通常这样命名 
全局常量 (defconstant limit (+ *glob* 1))
常量如果再次赋值就会报错 (如果赋的是原来的值则不会报错)

> (boundp '*glob*) 检查是否为全局常量或者变量 注意要引用

setf 用来给全局变量或局部变量赋值

(setf *glob* 98)
(let ((n 10))
  (setf n 2))

如果给出的符号事先不存在,就是默认创建全局变量   所以通过赋值可以隐式创建全局变量

但是一般还是使用  defparameter 来创建全局变量

第一个实参,还可以是表达式或变量名

(setf (car x) 'n)  返回 N      影响:  x=>(a b c)  原地修改 x 的值
--> x=>(n b c)

(setf a b
  c d
  e f)
相当于调用了三次 setf 赋值

函数式编程

意味着利用返回值而工作的程序 而不是修改东西  是lisp的主流范式
大部分内置函数被调用时为了取得返回值 而不是副作用

(remove 'a lst)  返回一个新的列表  原来的列表没有改变

函数式编程本质上避免使用 如setf这样的函数  程序中用到副作用的地方越少  你就更上一层楼

函数式编程最重要的有点之一是  他允许交互式测试

迭代

(defun show-squares(start end)           do宏
(do ((i start (+ i )))
((> i end) 'done)
(format t "~A ~A~%" i (* i i)))) 递归版本
(defun show-squares(i end)
(if (> i end)
'done
(progn
(format t "~A ~A~%" i (* i i))
(show-squares (+ i ) end)))) 更加简单的迭代操作
(defun our-length(lst)
(let ((len ))
(dolist (obj lst)
(setf len (+ len )))
len)) 返回的是 let的结果

递归版本
(defun our-length(lst)
  (if (null lst)
    0
    (+ 1 (our-length (cdr lst )))))   但是不是尾递归 效率不好

尾递归版本

(defun our-length(lst len)

  (if (null lst)

    len

    (our-length (cdr lst) (+ 1 len))))

函数作为对象
function 是特殊操作符 实参不用引用
(function +)        #'+ 作为函数的缩写 称之为升引号   #‘作为function来简写

apply 接受函数和实参列表 作为参数 来调用函数
(apply #'+ '(1 2 3))   ->6 可以接受任何数量的实参 只要最后一个是列表即可
(apply #'+ 2 1 '(1 2)) ->6

而 funcall 做的是一样的事 但是不需要把实参放在列表中
(funcall #'+ 1 2) ->3

lambda 只是一个符号 不是操作符

早期lambda存在的原因: 函数在内部使用列表表示    因此辨别列表 和函数的方法 就是检查第一个元素是否为lambda

现在看是否被引用  没有被引用的列表就视为函数

函数在内部会被表示成独特的函数对象,因此现在不需要lambda
((x) (+ x 100))   等价于
(lambda (x) (+ x 100))

((lambda(x) (+ x 1)) 1) -->2   匿名函数调用
(funcall #'(lambda(x) (+ x 100)) 1) -> 2 也是一样的

类型
变量没有类型  数值才有类型

虽然从来都不需要声明类型 但是出于效率来考虑  可能想要声明类型  后面讲

对象总是不止属于一个类型

类别的层级
类型 t 是所有类型的基类 所以每个对象都属于 t
fixnum integer rational real number atom t

(typep 23 'integer) 判断类型

展望

lisp语言用单一的语法来表达所有的程序结构

列表是一种lisp对象 函数本身也是lisp对象 函数能用列表来表示

lisp本身就是lisp程序  和内置的lisp函数没有任何区别

这开创了新的编程方法

ANSI Common lisp1的更多相关文章

  1. 简体中文 — ANSI Common Lisp 中文版

    简体中文 - ANSI Common Lisp 中文版 简体中文¶

  2. ANSI Common Lisp 中文翻譯版 — ANSI Common Lisp 中文版

    ANSI Common Lisp 中文翻譯版 — ANSI Common Lisp 中文版 ANSI Common Lisp 中文翻譯版¶

  3. ANSI Common Lisp Practice - My Answers - Chatper - 3

    Ok, Go ahead. 1 (a) (b) (c) (d) 2 注:union 在 Common Lisp 中的作用就是求两个集合的并集.但是这有一个前提,即给的两个列表已经满足集合的属性了.具体 ...

  4. ANSI Common Lisp Practice - My Answers - Chatper - 2

    I work out the questions by myself Chapter 2 question 4. (defun greater (x y) (if (> x y) x y ) ) ...

  5. ANSI Common Lisp Learn

    It has been a long time that I haven't dealt with my blog. On one hand I was preparing the exams.On ...

  6. MAC 下用 Common Lisp 调试 OpenGL 程序

    MAC 下用 Common Lisp 调试 OpenGL 程序 环境搭建 运行环境: OSX 10.11.3 EI Capitan Common Lisp: SBCL 使用 SBCL, 首先要安装这几 ...

  7. 在windows上安装common lisp开发环境

    (2014.1写于CSDN的文章) 最近对lisp非常感兴趣,因此在google中搜索了“common lisp install windows”, 想装一个开发环境玩玩. 第一条结果就是 “Gett ...

  8. Common Lisp学习资源整理

    Lisp Hackers: Interviews with 100x More Productive Programmers Posted on June 26th, 2013 Lisp Hacker ...

  9. 一道Common Lisp 宏的练习题

    这是<ANSI Common Lisp>第10章“宏”的习题第6题: 定义一个宏,接受一变量列表以及一个代码主体,并确保变量在代码主体被求值后恢复 (revert)到原本的数值

随机推荐

  1. 2019年上-C语言程序设计-第1次blog作业

    准备工作(20分) 1.在博客园申请个人博客,以真实姓名和学号加入班级博客(链接地址) 2.关注邹欣老师博客(并寻找答案:邹欣老师是谁?是做什么的?).关注任课老师博客 3.注册登录中国大学MOOC网 ...

  2. 第23课 优先选用make系列函数

    一. make系列函数 (一)三个make函数 1. std::make_shared:用于创建shared_ptr.GCC编译器中,其内部是通过调用std::allocate_shared来实现的. ...

  3. Destoon手机搜索点击提示 http 403 forbidden解决方法

    以下是网上搜到的答案: 最近发现用destoon开发的手机版网站,在手机版百度搜素网站的时候,点击之后出现 http 403 forbidden的弹出窗.必须再次的刷新网页才可以打开网站.出现这个问题 ...

  4. Knative 基本功能深入剖析:Knative Eventing 之 Sequence 介绍

    作者 | 元毅,阿里云容器平台高级开发工程师,负责阿里云容器平台 Knative 相关工作. 导读:在实际的开发中我们经常会遇到将一条数据需要经过多次处理的场景,称为 Pipeline.那么在 Kna ...

  5. Java程序使用Alpine Linux报错java.lang.NoClassDefFoundError: Could not initialize class org.xerial.snappy.Snappy解决

    报错内容 Caused by: java.lang.UnsatisfiedLinkError: /tmp/snappy-1.1.7-4a4b576a-c34c-481e-b6ac-9b4abacb11 ...

  6. 【模板整合计划】图论—有向无环图 (DAG) 与树

    [模板整合计划]图论-有向无环图 (DAG) 与树 一:[拓扑排序] 最大食物链计数 \(\text{[P4017]}\) #include<cstring> #include<cs ...

  7. .net架构的浅谈

    ,net的架构有以下几种 1.两层架构:UI + 数据层 2.三层架构:UI + 业务层 + 数据层 3.三层 + 接口层 (把相关的业务层抽象成接口,下层来实现接口,中层是依赖) 4.三层 + 接口 ...

  8. SQL 增、删、改、查语句

    1.SQL SELECT 语句 SELECT语句用于从表中选取数据. 结果被存储在一个结果表中(称为结果集). SQL SELECT语法 SELECT 列名称 FROM 表名称 以及 SELECT * ...

  9. C# 文件操作总结

    一.需求分析 1.将信息记录到本地记事本中. 2.将记录的信息读取出来. 3.计算出某个文件夹下所有后缀名为txt的数量和dll的数量. 4.从网络上下载文件. 二.二话不说上代码 using Sys ...

  10. P3028 汽水机(差分)

    题目 P3028 [USACO10OCT]汽水机Soda Machine 解析 差分,看到\(a[i]\leq 1e9\),离散化一下,在\(l\)处\(+1\),\(r+1\)处\(-1\),这样就 ...