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)到原本的数值
随机推荐
- redis 清除minerd进程的方法
redis 清除minerd进程的方法 1 修改redis配置文件 //禁止高危命令rename-command FLUSHALL ""rename-command CONFIG ...
- xunsearch强制刷新
$index = $xs->index; $index->flushLogging(); 等价于 util/Indexer.php --flush-log demo
- R语言dai xie
R语言,Python长期招代写,作业量充足,需要一定英文能力,价格满意.有意者请留言联系,谢谢
- 04、状态模式(State)
一.概念: 当一个对象的内在状态改变时,允许改变其行为,这个对象看起来像是改变了其类.[DP] 二.作用: 状态模式的主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况.吧状态的判断逻辑转 ...
- golang 学习笔记 -- struct interface的使用
一个 interface 类型定义了一个方法集做接口. 区分goalng的方法和函数 func go() { fmt.Println('go to home') } 这是函数 type car str ...
- Kafka学习笔记之Kafka背景及架构介绍
0x00 概述 本文介绍了Kafka的创建背景,设计目标,使用消息系统的优势以及目前流行的消息系统对比.并介绍了Kafka的架构,Producer消息路由,Consumer Group以及由其实现的不 ...
- 2019-11-29-WPF-测试触摸设备发送触摸按下和抬起不成对
原文:2019-11-29-WPF-测试触摸设备发送触摸按下和抬起不成对 title author date CreateTime categories WPF 测试触摸设备发送触摸按下和抬起不成对 ...
- java--Date时间
Date: 表示特定的瞬间,精确到毫秒,通过方法设定自己所表示的时间,可以表示任意的时间 System.currentTimeMillis() :返回的当前系统时间, 1970-1-1 至今的毫秒数 ...
- EF自动创建数据库步骤之一(实体类写法)
文章演示使用EF自动创建数据库第一个步骤创建实体类. 一.创建表映射实体类 using System; using System.Collections.Generic; using System.C ...
- c# 读数据库二进制流到图片
public Bitmap PictureShow(string connectionString, string opName, string productType) { ...