莆仙生活网
当前位置: 莆仙生活网 > 知识库 >

lisp

时间:2024-06-25 09:43:26 编辑:莆仙君

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 * 25> 55> 22Lisp并非以那种方式运行,运算符,如“+”不是在数字之间,而是在数字前面,就好像它们是函数名称一样。因此,如果你想将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 WorldHello World如果你希望在屏幕上打印一些内容,并返回其它内容,你应该使用打印函数:> (print Hello World)Hello WorldHello 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))0123456789NIL同样,重复一个列表也很简单:> (dolist (i '(0 1 2 3 4 5 6 7 8 9)) (print i))0123456789NIL以上两个函数都是DO函数的特殊版本,它就像在其它语言中组合使用while和for函数一样。它由三个部分组成:循环变更定义、终止条件和语句主体:> (do ((i 0 (+ 1 i))) ((> i 10)) (print i))012345678910NIL在这个例子中,变更定义部分为((i 0 (+ 1 i))),它定义变量i为0,并在每次循环时调用函数(+ 1 0)。终止条件为((> i 10)),表示在i大于10时函数终止运行。最后主体部分打印i的值。Lisp中也有条件函数,最基本的条件函数为if函数:> (if (> 10 20) (print Hello) (print World))WorldWorldif函数由三部分组成:条件、then语句和else语句。如果条件为真,则执行then语句,否则就执行else语句。你可能已经注意到,到现在为止我们仅使用了单个的语句——但如果你需要把几个语句连接在一起,那该怎么办呢?在Lisp中,要将几个语句连接起来,你需要使用progn这个特殊的控制流程函数:> (progn (print Hello) (print World))HelloWorldWorld例如,上例允许你在条件函数和循环中使用几个语句。如果你想要更进一步的了解和学习Common Lisp,可以搭配学习ANSI Common Lisp 和Practical Common Lisp (实用Common Lisp编程),如果你想再更进一步,可以看一看On Lisp 等参考书。

lisp语言的定义

约翰麦卡锡定义lisp当然早已有了各种计算模型--最著名的是图灵机. 但是图灵机程序难以读懂. 如果你要一种描述算法的语言,你可能需要更抽象的,而这就是约翰麦卡锡定义 Lisp的目标之一.约翰麦卡锡于1960年定义的语言还缺不少东西. 它没有副作用,没有连续执行 (它得和副作用在一起才有用),没有实际可用的数,[4] 没有动态可视域. 但这些限制可以令人惊讶地用极少的额外代码来补救. Steele和Sussman在一篇叫做``解释器的艺术''的著名论文中描述了如何做到这点.[5]如果你理解了约翰麦卡锡的eval,那你就不仅仅是理解了程序语言历史中的一个阶段. 这些思想至今仍是Lisp的语义核心. 所以从某种意义上,学习约翰麦卡锡的原著向我们展示了Lisp究竟是什么. 与其说Lisp是麦卡锡的设计,不如说是他的发现. 它不是生来就是一门用于人工智能,快速原型开发或同等层次任务的语言. 它是你试图公理化计算的结果(之一).随着时间的推移,中级语言,即被中间层程序员使用的语言,正一致地向Lisp靠近. 因此通过理解eval你正在明白将来的主流计算模式会是什么样.注释把约翰麦卡锡的记号翻译为代码的过程中我尽可能地少做改动. 我有过让代码更容易阅读的念头,但是我还是想保持原汁原味.在约翰麦卡锡的论文中,假用f来表示,而不是空表. 我用空表表示假以使例子能在Common Lisp中运行. (fixme)我略过了构造dotted pairs,因为你不需要它来理解eval. 我也没有提apply,虽然是apply(它的早期形式,主要作用是引用自变量),被约翰麦卡锡在1960年称为普遍函数,eval只是不过是被apply调用的子程序来完成所有的工作.我定义了list和cxr等作为简记法因为麦卡锡就是这么做的. 实际上 cxr等可以被定义为普通的函数. List也可以这样,如果我们修改eval,这很容易做到,让函数可以接受任意数目的自变量.麦卡锡的论文中只有五个原始操作符. 他使用了cond和quote,但可能把它们作为他的元语言的一部分. 同样他也没有定义逻辑操作符and和not,这不是个问题,因为它们可以被定义成合适的函数.在eval.的定义中我们调用了其它函数如pair.和assoc.,但任何我们用原始操作符定义的函数调用都可以用eval.来代替. 即(assoc. (car e) a)能写成(eval. '((label assoc.(lambda (x y)(cond ((eq (caar y) x) (cadar y))('t (assoc. x (cdr y))))))(car e)a)(cons (list 'e e) (cons (list 'a a) a)))麦卡锡的eval有一个错误. 第16行是(相当于)(evlis. (cdr e) a)而不是(cdr e),这使得自变量在一个有名函数的调用中被求值两次. 这显示当论文发表的时候,eval的这种描述还没有用IBM 704机器语言实现. 它还证明了如果不去运行程序,要保证不管多短的程序的正确性是多么困难.我还在麦卡锡的论文中碰到一个问题. 在定义了eval之后,他继续给出了一些更高级的函数--接受其它函数作为自变量的函数. 他定义了maplist:(label maplist(lambda (x f)(cond ((null x) '())('t (cons (f x) (maplist (cdr x) f))))))然后用它写了一个做微分的简单函数diff. 但是diff传给maplist一个用x做参数的函数,对它的引用被maplist中的参数x所捕获.[6]这是关于动态可视域危险性的雄辩证据,即使是最早的更高级函数的例子也因为它而出错. 可能麦卡锡在1960年还没有充分意识到动态可视域的含意. 动态可视域令人惊异地在Lisp实现中存在了相当长的时间--直到Sussman和Steele于 1975年开发了Scheme. 词法可视域没使eval的定义复杂多少,却使编译器更难写了.About this documentAbout this document ... This document was generated using the LaTeX2HTML translator Version 2K.1beta (1.48)Copyright © 1993,1994,1995,1996,Nikos Drakos,Computer Based Learning Unit,University of Leeds.Copyright © 1997,1998,1999,Ross Moore,Mathematics Department,Macquarie University,Sydney.The command line arguments were:latex2html -split=0 roots_of_lisp.texThe translation was initiated by Dai Yuwen on 2003-10-24 [1]欧几里德对几何的贡献.``Recursive Functions of Symbolic Expressions and Their Computation by Machine,Part1.'' Communication of the ACM 3:4,April 1960,pp. 184-195.[2]当表达式以七个原始操作符中的五个开头时,它的自变量总是要求值的.[3]以另外两个操作符quote和cond开头的表达式以不同的方式求值. 当 quote表达式求值时,它的自变量不被求值,而是作为整个表达式的值返回. 在 一个正确的cond表达式中,只有L形路径上的子表达式会被求值.[4]逻辑上我们不需要为了这定义一个新的记号. 在现有的记号中用 一个叫做Y组合器的函数上的函数,我们可以定义递归函数. 可能麦卡锡在写 这篇论文的时候还不知道Y组合器; 无论如何,label可读性更强.没有实际可用的数,在麦卡锡的1960 年的Lisp中,做算术是可能的,比如用一个有n个原子的表表示数n.... 的艺术''的著名论文中描述了如何做到这点.5Guy Lewis Steele,Jr. and Gerald Jay Sussman,``The Art of the Interpreter,or the Modularity Complex(Parts Zero,One,and Two),'' MIT AL Lab Memo 453,May 1978.... 对它的引用被maplist中的参数x所捕获.6当代的Lisp程序 员在这儿会用mapcar代替maplist. 这个例子解开了一个谜团: maplist为什 么会在Common Lisp中. 它是最早的映射函数,mapcar是后来增加的.