ANSI Common lisp1
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的更多相关文章
- 简体中文 — ANSI Common Lisp 中文版
简体中文 - ANSI Common Lisp 中文版 简体中文¶
- ANSI Common Lisp 中文翻譯版 — ANSI Common Lisp 中文版
ANSI Common Lisp 中文翻譯版 — ANSI Common Lisp 中文版 ANSI Common Lisp 中文翻譯版¶
- ANSI Common Lisp Practice - My Answers - Chatper - 3
Ok, Go ahead. 1 (a) (b) (c) (d) 2 注:union 在 Common Lisp 中的作用就是求两个集合的并集.但是这有一个前提,即给的两个列表已经满足集合的属性了.具体 ...
- 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 ) ) ...
- 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 ...
- MAC 下用 Common Lisp 调试 OpenGL 程序
MAC 下用 Common Lisp 调试 OpenGL 程序 环境搭建 运行环境: OSX 10.11.3 EI Capitan Common Lisp: SBCL 使用 SBCL, 首先要安装这几 ...
- 在windows上安装common lisp开发环境
(2014.1写于CSDN的文章) 最近对lisp非常感兴趣,因此在google中搜索了“common lisp install windows”, 想装一个开发环境玩玩. 第一条结果就是 “Gett ...
- Common Lisp学习资源整理
Lisp Hackers: Interviews with 100x More Productive Programmers Posted on June 26th, 2013 Lisp Hacker ...
- 一道Common Lisp 宏的练习题
这是<ANSI Common Lisp>第10章“宏”的习题第6题: 定义一个宏,接受一变量列表以及一个代码主体,并确保变量在代码主体被求值后恢复 (revert)到原本的数值
随机推荐
- guava(一)Preconditions
工具类 就是封装平常用的方法,不需要你重复造轮子,节省开发人员时间,提高工作效率.谷歌作为大公司,当然会从日常的工作中提取中很多高效率的方法出来.所以就诞生了guava.. 高效设计良好的API,被G ...
- FontForge:免费字库设计软件 附使用教程
引用:http://www.sucaijishi.com/2018/articles_0817/259.html 如何设计一套自己的字库?今天分享一个开源的字库设计软件FontForge, 官方下载: ...
- Qt Quick小项目 - 登陆界面
开发环境: win8 + Qt5.11.2 说明: 用 QML 设计一个应用的登陆界面. 效果图: 新建一个 "Qt Quick Application - empty" 工程,分 ...
- mac解决安装提示“xxx软件已损坏,打不开,您应该将它移到废纸篓”的提示
如果没有“任何来源”选项则运行: macOS Sierra设置说明 若已安装了最新系统 macOS Sierra 则有可能出现某些安装包已损坏.显示未激活.打开崩溃等的提示!!原因是因为新系统屏蔽了任 ...
- 02、策略模式(Strategy)
一.概念: 策略是为达到某一目的而采取的手段或方法,策略模式的本质是目标与手段的分离, 手段不同而最终达成的目标一致.客户只关心目标而不在意具体的实现方法, 实现方法要根据具体的环境因素而变化. 二. ...
- 【linux】查看linux系统自带的服务启动文件
=============================================================== 1.查看所有启动文件 systemctl list-unit-files ...
- .NET[C#]使用LINQ从List<T>集合中获取最后N条数据记录的方法有哪些?
https://codedefault.com/2018/using-linq-to-get-the-last-n-elements-of-a-collection-in-csharp-applica ...
- .NET CORE 控制台应用程序配置log4net日志文件
使用文件格式记录日志 1.新建一个.NET CORE控制台应用程序,添加log4net.dll引用,打开工具->NuGet包管理器->管理解决方案的NuGet程序包. 2.在NuGet-解 ...
- PIE SDK坐标转换算法
1.算法功能简介 数据坐标转换是空间实体位置的位置描述,是从一种坐标系统变换到另一坐标系统的过程.通过建立两个坐标系之间一一对应关系来实现.是各种比例尺地图测量和编绘中建立地图数学基础必不可少的步骤. ...
- Git本地有未提交文件,直接拉取远端最新版本
git pull = git fetch + git merge 1.修改不同的文件: 用户D和用户L在本地提交中修改了不同的文件,如果用户D将改动推送到服务器后,用户L再推送就会遇到非快进式推送错误 ...