commonlisp编译器
A. lisp语言的快速入门
Common Lisp语言快速入门: Lisp是软件领域的分裂力量。一方面,Lisp爱好者誓言Lisp比软件领域内的其它语言都更加快捷、整洁和强大;而反对者则辩称,不可靠的执行和库支持不足使得开发者难以在其中编写任何真正的软件。事实上,他们都有各自的道理。
第一版Lisp于大约50年前推出,这使得它和FORTRAN一样,成为现在仍在使用的最古老的编程语言之一。可以证明,它拥有(它们将会拥有)最庞大的特性列表,它也是第一种包括一整套今天我们全都认为是标准语言特性的语言,如垃圾收集、递归、函数作为对象、甚至是普通的if-then-else子句。同时,人们也认为它是一款优秀的教学语言:MIT使用方案、Lisp衍生物,传授它们的介绍性编程类。
我们将一起学习最强大和项目就绪的Lisp版本:Common Lisp,使其正常运行,并了解一些Lisp应用。 代码和数据并无差异——在Lisp中,代码只是一个函数对象列表。源代码和数据源之间不存在区别,允许Lisp把它的内在呈现给编译器、注释器和程序员。这允许你方便地本地读入和评估代码,甚至可以允许你使用宏。
宏——定义和重新定义任何语言元素。不喜欢条件、或循环或函数的运行方式吗?好,你可以定义自己的注释。如果你在代码中多次执行某个特定的任务,把那个特性添加到语言中不是更加方便吗?应用Lisp,你可以实现上述功能。
速度——虽然在许多情况下Common Lisp不如C或OCaml这类速度巨人快捷,但它在一系列测试中表现良好,特别是在执行一小段程序的情况下。了解一些基本的编译器知识,你就能够编写出处理列表和大型数字的代码,在执行速度和内存使用方面都要优于其它语言。
简化——Lisp的一切功能都基于一些基本的理念——一旦你了解那些理念,你就几乎能够处理任何问题。Lisp程序员常常自夸说,仅仅用几百行代码,你就能在几乎任何语言(如C++或Haskell)中执行一个Lisp注释器。
灵活——以你喜欢的任何方式编写代码。更喜欢功能性的编程方法吗?没问题!想要完全反复编程吗?草草写下几个快速的宏就可以完成任务。你可以用最方便最高效的方式编写程序,而且这些程序都能良好地运行。 好,行!我选择了Lisp,我如何进行安装呢?
这是棘手的问题。不像是Python或C#一样,Common Lisp没有标准执行——该语言由一个规范,而不是执行来定义。Common Lisp也没有C语言的特点,及在每一个平台上都是一种支配性的执行或流行的执行。每个版本都应执行上述标准,但有一些细节要由编译器或注释器来处理,这使得每个执行都稍有不同。
你可以使用几种选项——在本文中我使用CLISP,它在Windows、Linux和Mac(仅PPC)上运行良好。如果你使用英特尔Mac,则必须使用其它执行,如Allegro Common Lisp或SBCL。对于这篇快速入门中的简单例子,你使用哪个执行都不要紧。
使用Lispbox可以快速安装Common Lisp系统,但遗憾的是,Lispbox在2012年停止了更新。Lispbox为你提供一个Common Lisp执行、Emacs、SBCL和Slime——Emacs高级Lisp整合模式,许多Lisp程序员会告诉你说,这个组合是使用Lisp的唯一方法。如果你并非Emacs用户(我本人也和你一样),不要担心,它并非必要条件,只是使得编写Common Lisp程序更加简便。而如果想要使用最新的Emacs、SBCL和Slime,网络上也有许多教程关于如何配置它们。
安装过程因平台而异,在Windows中你只能运行安装程序,多数Linux软件包管理器提供安装包等。选择一个执行并遵循安装指令即可。打开Common Lisp (CLisp)交互环境的方式是M-x slime,如果你不理解这是什么意思的话可以通过Ctrl+h松开后按t来学习Emacs的基本操作。但由于Emacs的默认字体不支持中文,所以为了避免教程乱码,点击程序窗口左上角的Options,选择Set Default Font,选择支持中文的字体后保存。打开REPL(交互式提示符)之后再返回这里,我们继续往后讲。 REPL代表“阅读-评估-打印-循环”(Read-Evaluate-Print-Loop),它简单表示注释器的一个交互式提示符。你可以从这里输出一些简单的Lisp代码。如果你使用另一种注释器提示符,你可以在这里使用提示符作为计算器输出一些基本的数学表达式——但它不能正常运行。在CLISP中输入5*2不会返回任何有意义的结果:
> 5 * 2
5
> 5
5
> 2
2
Lisp并非以那种方式运行,运算符,如“+”不是在数字之间,而是在数字前面,就好像它们是函数名称一样。因此,如果你想将REPL当作计算器使用,你必须输入:
> (* 5 2)
10
> (+ 1 2 3 4)
10
> (+ (* 5 2) (* 10 3) (/ 100 4))
65
对你来说,理解这种用法可能更难一些,但它拥有一些优势:它便于编译器解析、它对所有函数和运算符都一样、它让你给函数添加尽可能多的自变量——例如,在上面的第二个例子中,你可以任意扩充加数,使得加法函数和总计函数完全一样。
另外你会注意到,函数名在括号内,而不像许多其它语言一样函数名在括号以外。这表示你要写(函数自变量)而不是函数(自变量)。
每个Lisp表达式会返回一个值,一个函数总是返回最后一个表达式的结果——即使是NIL,NULL在Java或C++中的对等值也是这样。因此在Lisp中显示“Hello World”相当简单:
> Hello World
Hello World
如果你希望在屏幕上打印一些内容,并返回其它内容,你应该使用打印函数:
> (print Hello World)
Hello World
Hello World
这个字符串显示两次,一个是打印结果,一个是函数返回的结果。
Lisp表示LIST Processor(列表处理器),Lisp中的几乎所有内容都以列表的形式存在,因此有时你必须处理列表。定义列表非常容易:
> (list 1 2 3 4 5)
(1 2 3 4 5)
> '(1 2 3 4 5)
(1 2 3 4 5)
第二种定义方法叫做引用,除定义简单的列表外,它还有更多用途,不过我们必须在后面的另一篇文章中讨论那个主题。 Lisp拥有全部标准控制流程方法。定义一个重复一个值的简单循环相当容易:
> (dotimes (i 10) (print i))0
1
2
3
4
5
6
7
8
9
NIL
同样,重复一个列表也很简单:
> (dolist (i '(0 1 2 3 4 5 6 7 8 9)) (print i))
0
1
2
3
4
5
6
7
8
9
NIL
以上两个函数都是DO函数的特殊版本,它就像在其它语言中组合使用while和for函数一样。它由三个部分组成:循环变更定义、终止条件和语句主体:
> (do ((i 0 (+ 1 i))) ((> i 10)) (print i))
0
1
2
3
4
5
6
7
8
9
10
NIL
在这个例子中,变更定义部分为((i 0 (+ 1 i))),它定义变量i为0,并在每次循环时调用函数(+ 1 0)。终止条件为((> i 10)),表示在i大于10时函数终止运行。最后主体部分打印i的值。
Lisp中也有条件函数,最基本的条件函数为if函数:
> (if (> 10 20) (print Hello) (print World))
World
World
if函数由三部分组成:条件、then语句和else语句。如果条件为真,则执行then语句,否则就执行else语句。
你可能已经注意到,到现在为止我们仅使用了单个的语句——但如果你需要把几个语句连接在一起,那该怎么办呢?在Lisp中,要将几个语句连接起来,你需要使用progn这个特殊的控制流程函数:
> (progn (print Hello) (print World))
Hello
World
World
例如,上例允许你在条件函数和循环中使用几个语句。
如果你想要更进一步的了解和学习Common Lisp,可以搭配学习ANSI Common Lisp 和Practical Common Lisp (实用Common Lisp编程),如果你想再更进一步,可以看一看On Lisp 等参考书。
B. 如何评价 Racket 这门编程语言
Racket的诞生与发展
简单介绍一下Racket的发展,详见知乎的一个关于Racket的问题回答:
1958年,人工智能之父John McCarthy 发明了一种以 Lambda 演算为基础的符号处理语言,1960年 McCarthy 发表着名论文Recursive Functions of Symbolic Expressions and Their Computation by Machine, 从此这种语言被命名为 LSIP (List Processor),其语法被命名为:符号表达式(S-Expression)。LISP构建在7个函数[atom car cdr cond cons eq quote]和2个特型[lambda label]之上。
Lisp诞生之初是为了纯粹的科学研究,代码执行像数学公式一样,以人的大脑来演算。直到麦卡锡的学生斯蒂芬·罗素将eval函数在IBM 704机器上实现后,才开启了Lisp作为一种计算机语言的历史。1962年,第一个完整的Lisp编译器在MIT诞生,从此之后Lisp以MIT为中心向全世界传播。之后十多年,出现了各种Lisp方言。
1975年,Scheme诞生。Scheme同样诞生与MIT,它的设计哲学是最小极简主义,它只提供必须的少数几个原语,所有其他的实用功能都由库来实现。在极简主义的设计思想下,Scheme趋于极致的优雅,并作为计算机教学语言在教育界广泛使用。
1984年,Common Lisp诞生。在二十世纪七八十年代,由于Lisp方言过多,社区分裂,不利于lisp整体的发展。从1981年开始,在一个Lisp黑客组织的运作下,经过三年的努力整合后,于1984年推出了Common Lisp。由于Scheme的设计理念和其他Lisp版本不同,所以尽管Common Lisp借鉴了Scheme的一些特点,但没有把Scheme整合进来。此后Lisp仅剩下两支方言: Common Lisp 和 Scheme。
从二十世纪九十年代开始,由于C++、Java、C#的兴起,Lisp逐渐没落。直到2005年后,随着科学计算的升温,动态语言JavaScript、Python、Ruby的流行,Lisp又渐渐的回到人们的视线。不过在Lisp的传统阵地教育界,Python作为强有力的挑战者对Scheme发起冲锋;在2008年,MIT放弃了使用Scheme作为教学语言的SICP(计算机程序的构造和解释)课程,而启用Python进行基础教学。同时美国东北大学另立炉灶,其主导的科学计算系统PLT Scheme开始迅猛发展;2010年,PLT Scheme改名为Racket。近几年,The Racket Language连续成为年度最活跃语言网站,并驾齐驱的还有haskell网站。
符号表达式 S-Expression
首先说一下S表达式:S-表达式的基本元素是list与atom。list由括号包围,可包涵任何数量的由空格所分隔的元素,原子是其它内容。其使用前缀表示法,在Lisp中既用作代码,也用作数据。如:1+2*3 写成前缀表达式就是(+ 1 (* 2 3)) 。
优点:容易parse,简单纯粹,不用考虑什么优先级等,也是实现代码即数据的前提;
缺点:可读性不是很强;
高阶函数
高阶函数至少满足下列一个条件:
接受一个或多个函数作为输入;
输出一个函数;
微积分中的导数就是一个例子,映射一个函数到另一个函数。在无类型 lambda 演算中,所有函数都是高阶的。在函数式编程中,返回另一个函数的高阶函数被称为Curry化的函数。Curry化即把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。如 f(x,y)=x+y, 如果给定了 y=1,则就得到了 g(x)=x+1 这个函数。
Lambda 表达式
Racket中实用Lambda表达式来定义匿名函数,《如何设计程序》书中给出的使用原则是:如果某个非递归函数只需要当参数使用一次,实用Lambda表达式。如果想用Lambda表达式来表达递归,就需要引入Y组合子,Y 就是这样一个操作符,它作用于任何一个 (接受一个函数作为参数的) 函数 F,就会返回一个函数 X。再把 F 作用于这个函数 X,还是得到 X。所以 X 被叫做 F 的不动点(fixed point),即 (Y F) = (F (Y F)) 。
惰性求值
惰性求值(Lazy Evaluation),说白了就是某些中间结果不需要被求出来,求出来反而不利于后面的计算也浪费了时间。参见:惰性求值与惰性编程。
惰性求值是一个计算机编程中的一个概念,它的目的是要最小化计算机要做的工作。惰性计算的最重要的好处是它可以构造一个无限的数据类型。使用惰性求值的时候,表达式不在它被绑定到变量之后就立即求值,而是在该值被取用的时候求值。语句如 x:=expression; (把一个表达式的结果赋值给一个变量)明显的调用这个表达式并把计算并把结果放置到 x 中,但是先不管实际在 x 中的是什么,直到通过后面的表达式中到 x 的引用而有了对它的值的需求的时候,而后面表达式自身的求值也可以被延迟,最终为了生成让外界看到的某个符号而计算这个快速增长的依赖树。
闭包
闭包在计算机科学中,闭包(Closure)是词法闭包(Lexical Closure)的简称,是引用了自由变量的函数。自由变量是在表达式中用于表示一个位置或一些位置的符号,比如 f(x,y) 对 x 求偏导时,y就是自由变量。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。在函数中(嵌套)定义另一个函数时,如果内部的函数引用了外部的函数的变量,则可能产生闭包。运行时,一旦外部的 函数被执行,一个闭包就形成了,闭包中包含了内部函数的代码,以及所需外部函数中的变量的引用。其中所引用的变量称作上值(upvalue)。网上有很多将JavaScript闭包的文章,如果你对LISP有系统的了解,那么这个概念自然会很清楚了。
快排的Racket实现
#langracket
(define(quick-sortarray)
(cond
[(empty?array)empty];快排的思想是分治+递归
[else(append
(quick-sort(filter(lambda(x)(<x(firstarray)))array));这里的array就是闭包
(filter(lambda(x)(=x(firstarray)))array)
(quick-sort(filter(lambda(x)(>x(firstarray)))array)))]))
(quick-sort'(132534509824))
;;运行结果'(012334455982)
通过这个例子,就可以感受到基于lambda算子的 Racket 语言强大的表达能力了,高阶函数、lambda表达式和闭包的使用是Racket所描述的快排十分的精炼,这和 基于冯诺依曼模型C语言是迥然不容的思维模式。后面,随着Racket 学习的进一步深入,尝试写一下解释器
C. 请问LISP编程语言的编译器是用什么语言编写的
LISP(全名LISt Processor,即链表处理语言),由约翰·麦卡锡在1960年左右创造的一种基于λ演算的函数式编程语言。 ——网络
使一种函数编程语言,曾被用于人工智能,语言都是一些符号,函数,很简单的语言(因此也做不出什么程序来或是很难做出,更别谈人工智能),不需要英语基础。
D. LISP代码是什么
LISP(全名LISt Processor,即链表处理语言),由约翰·麦卡锡在1960年左右创造的一种基于λ演算的函数式编程语言。
LISP有很多种方言,各个实现中的语言不完全一样。各种LISP方言的长处在于操作符号性的数据和复杂的数据结构。1980年代Guy L. Steele编写了Common Lisp试图进行标准化,这个标准被大多数解释器和编译器所接受。在Unix/Linux系统中,还有一种和Emacs一起的Emacs Lisp(而Emacs正是用Lisp编写的)非常流行,并建立了自己的标准。
LISP的祖先是1950年代Carnegie-Mellon大学的Newell、Shaw、Simon开发的IPL语言。
LISP语言的主要现代版本包括Common Lisp和Scheme。
lisp拥有理论上最高的运算能力
1 基本介绍
Lisp的表达式是一个原子(atom)或表(list),原子(atom)是一个字母序列,如abc;表是由零个或多个表达式组成的序列,表达式之间用空格分隔开,放入一对括号中,如:
abc
()
(abc xyz)
(a b (c) d)
最后一个表是由四个元素构成的,其中第三个元素本身也是一个表。
正如算数表达式1+1有值2一样,Lisp中的表达式也有值,如果表达式e得出值v,我们说e返回v。如果一个表达式是一个表,那么我们把表中的第一个元素叫做操作符,其余的元素叫做自变量。
Lisp的7个公理(基本操作符):
(quote x)返回x,我们简记为'x
(atom x)当x是一个原子或者空表时返回原子t,否则返回空表()。在Lisp中我们习惯用原子t表示真,而用空表()表示假。
> (atom 'a)
t
> (atom '(a b c))
()
> (atom '())
t
现在我们有了第一个需要求出自变量值的操作符,让我们来看看quote操作符的作用——通过引用(quote)一个表,我们避免它被求值。一个未被引用的表达式作为自变量,atom将其视为代码,例如:
> (atom (atom 'a))
t
反之一个被引用的表仅仅被视为表
> (atom '(atom 'a))
()
引用看上去有些奇怪,因为你很难在其它语言中找到类似的概念,但正是这一特征构成了Lisp最为与众不同的特点——代码和数据使用相同的结构来表示,而我们用quote来区分它们。 (eq x y)当x和y的值相同或者同为空表时返回t,否则返回空表()
> (eq 'a 'a)
t
> (eq 'a 'b)
()
> (eq '() '())
t
(car x)要求x是一个表,它返回x中的第一个元素,例如:
> (car '(a b))
a
(cdr x)同样要求x是一个表,它返回x中除第一个元素之外的所有元素组成的表,例如:
> (cdr '(a b c))
(b c)
(cons x y)要求y是一个表,它返回一个表,这个表的第一个元素是x,其后是y中的所有元素,例如:
> (cons 'a '(b c))
(a b c)
> (cons 'a (cons 'b (cons 'c ())))
(a b c)
2 Common LISP的Hello World程序
下面是一个在标准输出设备上输出Hello World的简单程序,这种程序通常作为开始学习编程语言时的第一个程序:
(format t "Hello, world!~%")
E. lisp有什么比较好用的编译器么
如果你是学的是common lisp的话:可以使用 lisp in a box。
lisp in a box 是一个在emacs的基础上扩展的ide
如果你是学的是autolisp的话:可以使用 cad里面自带的vlisp开发环境。
在cad的命令行输入vlisp或者vide后回车即可进入。
如果你是学的是elisp的话:可以 直接使用emacs。
如果你是学的是scheme的话:可以 直接使用ChezScheme
如果你想自己动手做一个的话可以参考sicp后几章或者看看王垠的如何写解释器那篇文章