通向Lisp的道路(2018)

2021-02-27 09:58:20

我收到了大量电子邮件,要求提供有关如何学习Common Lisp的建议。我决定写下我通过电子邮件和社交媒体帖子提供的所有建议,希望有人会发现它有用。

前面有一个免责声明:这是通向Lisp的道路,而不是通向Lisp的道路。这是我所遵循的(没有一些死胡同),并且吸收了我很多个人见解,但这绝不是学习语言的唯一方法。

我认为,在开始学习Common Lisp之前,先了解一下Lisp的来历和语言是很重要的。如果您直接来自现代语言,有些事情看起来会很奇怪,但是如果您有一些背景知识,这些事情将变得更加有意义。

常见的Lisp有着悠久而深刻的历史。我不会在这里尝试覆盖所有内容-如果您有兴趣,请检查以下内容(按大致递增的顺序):

我知道您可能不想立即阅读上面的所有链接,因此这里是Lisp六十年的旋风之旅。

在接下来的二十年左右的时间里,Lisp的各种版本和方言蓬勃发展。一些比较著名的方言是Maclisp,BBN Lisp / Interlisp,Franz Lisp,Spice Lisp和Lisp Machine Lisp。也有其他人。关键是存在许多不同的实现,所有实现都在不断增长,更改和尝试不同的东西。

(该计划也起源于这个时间范围,但采取了截然不同的路线,并且与我们正在寻找的路径有所不同。在这篇文章中,我将不介绍Scheme。)

在1980年代初期,人们认为拥有大量互不兼容的Lisp方言可能不是理想的选择。努力采用这些有机增长的不同语言,并产生一种可以满足所有人(或至少是“每个人”的合理子集)需求的通用语言。 1984年,Guy Steele的《共同的Lisp:语言》第一版出版了。

如果您做一些数学运算,您会发现本书出版时Lisp大约有25年的实际使用,实验,经验和历史可供借鉴。即便如此,仅此本书并不能使所有人都满意,并且在1986年成立了一个委员会(X3J13)来为Common Lisp制定ANSI规范。

当委员会致力于标准化过程时,1990年发布了第二版Common Lisp:语言。这更加全面,包含了委员会正在处理的一些事情(有关更多信息,请参见上面链接的comp.lang.lisp常见问题解答)。在这一点上,Lisp语言家族已有30多年的经验和历史可以借鉴。为了进行比较:Python(一种现代语言,许多人认为也是古老的语言)在下一年首次发布。

X3J13委员会在1992年发布了新的Common LispANSI标准的初稿,以供公众审查(请参阅Pitman的论文)。该草案于1994年获得批准,批准的规范最终于1995年发布。在这一点上,Lisp已有35年的历史了。 Ruby的第一个版本于当年12月发布。

历史课到此结束。尚未对Common Lisp的ANSI规范进行另一修订。如果您看到某个自称为“ Common Lisp的实现”的东西,则该版本于1995年发布,至今仍在使用。今天,这就是它所指的规范。

我想向您简要介绍Common Lisp的历史,因为我想让您知道自己正在从事什么。我希望您认识到Common Lisp是一种稳定,庞大,实用,可扩展,丑陋的语言。了解这些特性会使您在学习该语言时很多事情变得更有意义,并且我想在此之前先对它们进行更多的讨论我开始提供建议。

如果您来自其他语言,则您可能会习惯于在升级时遇到的麻烦。您的语言实现和/或库。如果要运行十年前在最新版本的Ruby上编写的Ruby代码,则可能需要花费一些精力来对其进行更新。我目前的工作是在Scala,如果图书馆的最后一项活动在Github上超过2或3年,我只是假设如果没有很大的麻烦,它就无法工作。在大多数现代语言中,我们每天处理的向后不兼容之轮是生活中的事实,尽管某些语言肯定比其他语言更好。

如果您学习Common Lisp,通常情况并非如此。在这篇文章的下一部分中,我将推荐一本1990年编写的书。您可以在上个月发布的Common Lisp实现中运行其代码,并且保持不变。在经历了向后不兼容的仓鼠滚轮上的多年磨合之后,我无法告诉您能够编写代码并合理地期望它在20年后仍能继续工作是多么的轻松。

当然,这仅是语言本身的情况-如果您依赖任何库,那么在更新它们时,它们总是有可能损坏。但是我发现核心语言的稳定性具有传染性,并且总体而言,Common Lisp社区在保持向后兼容性方面似乎还不错。

我会说实话:有例外。当您学习语言并开始使用库时,您会开始注意到一些不愿意为它们的库记录和保留稳定的API的库作者,并且如果远离仓鼠轮对您很重要,您将学习到尽可能避免依赖那些人编写的代码。

关于Common Lisp的另一件需要了解的是,它是一种实用的大型语言。 《 Common Lisp:语言》的第二版(通常由Common Lisp程序员缩写为" CLtL2")长971页,不包括前言,参考文献或索引。在没有太多额外支持的情况下,编写纯Common Lisp可以使您获得惊人的收益。

在Common Lisp中编程应用程序时,人们通常会依赖少量的稳定库,而库作者经常尝试通过利用尽可能多的核心语言来最大限度地减少依赖关系。我尝试将依赖关系保持在少于十个左右我的应用程序,我的库最多不超过2到3个(如果可能的话,最好是0个),但是我可能比大多数人保守一些。我真的不喜欢仓鼠轮。

还要注意的是,由于Common Lisp一直存在并且很稳定,所以它的库比许多编程语言都更旧和更稳定,例如:Bordeaux Threads(Common Lisp的事实上的线程库)是第一个它在2004年提出并在不久之后发布(最迟在2006年发布,但可能更早发布了,因为现在已经有很多链接死了,所以很难说),这使得它已经有14年的历史了。所以是的,线程是由一个库处理的,但是我不担心它会在接下来的一两年内破坏我的代码。

我的建议是:当您学习Common Lisp并查找库时,请尝试抑制您脑后的声音,说“"该项目的最新更新是六年前?”。那可能是被抛弃和破坏的。 Common Lisp的稳定性意味着有时候可以只完成库,而不是废弃库,所以不要一味地将它们解散。

Common Lisp实用性的一部分来自其可扩展性。没有人要求新版本的规范增加功能,因为Common Lisp的可扩展性允许用户以普通的旧库的形式向语言添加新功能,而无需更改核心语言。您会听到Lisp的可扩展性,当然这也是其中的一部分。宏允许用户编写可能是其他语言的核心语言功能的库。

Common Lisp不包括字符串插值。你想要它?没问题,您不必等待Scala2.10或Python 3.6,只需使用一个库即可。

模式匹配语法可以编写出非常漂亮,可读的代码。CommonLisp不包含它,但是当然有一个库。

所有这些库都依靠宏来使使用它们变得无缝。当然,您可以不使用宏来完成所有这些操作,但是您必须添加一层样板来管理评估。这:

(匹配foo'(list x y z)(lambda(x y z)(+ x y z))'(vector x y)(lambda(x y)(-x y)))

(匹配foo((list x y z)(+ x y z))((vector x y)(-x y)))

没有人会努力尝试获得Common Lisp标准的新修订版以添加模式匹配,因为您可以将其编写为库,并获得90%或更多的内置代码。该语言为您提供了足够的功能来扩展它,就好像扩展是从一开始就存在的那样。

由库提供其他语言的核心功能可能与上一章有关最小化依赖关系不一致,并且在某种程度上是正确的。但是,我认为有一种快乐的媒介,您可以用核心语言编写稳定的库,然后依靠应用程序中的少数这些库来添加任何特定问题所需的功能。

宏是使Lisp如此可扩展的原因之一,因为它们使您可以将任意代码转换为其他任意代码。对于像C这样的宏语言也是如此,但是Common Lisp宏是不同的,因为它们是语言的一部分。

在C语言中,您在顶层有一层宏,这些宏是用预处理程序宏语言编写的。宏层和语言层是相互独立的,宏层提供了一种额外的抽象能力(别误会,这肯定是有用的)。

在Common Lisp中,您可以在Common Lisp本身中编写宏。然后,您可以使用这些宏编写函数,然后使用这些宏编写更多的宏,而不是两个分层的层,而是一个抽象能力的反馈回路。

但是,宏并不是Common Lisp唯一使它如此实用和可扩展的东西。人们常常没有意识到的是,尽管Common Lisp由于使用了宏而成为一种非常高级的语言,但它也具有许多作为语言一部分的低级功能。它永远不会像C,Rust或Forth这样低级,但是您可能会对ANSI规范中包含的某些东西感到惊讶。

需要将未装箱的浮子阵列运送到图形卡吗?该标准允许这样做。

认为GOTO应该被认为是有帮助的,不是有害的吗?好吧,我们在这里都是大人。祝你好运,不要脚踩脚。

是否需要在Game Boy模拟器中执行无符号的8位算术运算,但更喜欢将其编译为一两条机器指令?这是可能的。

并非所有Common Lisp实现实际上都执行所有这些优化,但是Common Lisp的设计人员具有预见性,需要包括支持它们的语言功能。您可以编写标准定义的普通Common Lisp,并相信它将在任何地方运行,并且支持这些事情的实现将利用优化机会。

支持带有宏的极高级编程和合理数量的低级优化的结合意味着,即使规范已经使用了20多年了,它仍然是当今构建的良好基础。设计师从中汲取的30年经验和历史使他们能够创建一种非常实用的语言,并在数十年的历史中幸存下来。

同样重要的是要认识到,尽管Common Lisp可能非常实用,但是容纳现有用户和方言的需求意味着有很多丑陋的部分。如果您购买了第二版《 Common Lisp:The Language》的纸质副本,并查找了“ kludges”在索引中,您会发现:

Common Lisp并不是编程语言设计的美丽结晶。这是一个sc脚的车间,上面有一堵大钉板墙,地板上有一薄层木屑,办公室里有一个文件柜,柜子里有几个垂直于其余部分的抽屉,还有一个看起来很奇怪的锯子。 RPLACD写在坐在角落的那一侧,二十年来没有人碰过它。

此历史包is是为确保Common Lisp拥有未来而付出的代价。这使得使用旧方言的人们以合理的努力实际采用CommonLisp成为现实。如果设计人员试图使其完美,美观,那么可能会使它与端口实现和代码过于不同,并可能导致该语言被忽略,而不是被采用和接受。

如果所有这些都没有让您害怕该语言,那么让我们来谈谈您如何在2018年学习它。

如果您在Internet上搜索Common Lisp教程和指南,您将找不到想要的东西。这是因为在Internet诞生之初或之初就创建了许多Common Lisp参考资料。那里有很多有关Common Lisp的书。有些比其他更好。我会推荐我认为最好的那些,但是不要四处浏览并找到其他人。

要开始使用Common Lisp,您需要安装Common Lispimplementation。 Common Lisp是ANSI规范,因此它有多种实现,您可以选择。有很多选择,但我将为您简化一下:

如果您使用的是MacOS,并且希望可以从App Store下载单个GUI应用程序,请选择ClozureCL(通常缩写为" CCL")。

那个带Z. Clojure的Clozure是完全不同的东西,只是具有一个令人困惑的相似名称。

您可能还会听到称为CLISP的声音,这听起来像是您想要的。不是。 CLISP只是另一种实现,但是它已经发布了八年(即使其源存储库仍在开发中!),它不像CCL或SBCL那样常用。如果对安装等有疑问,将很难找到帮助。

您可能还会听到有关Roswell的信息。不要使用Roswell,您不需要(但(或完全没有))。

现在只需安装SBCL或CCL,一旦您对轴承有了更好的了解,就可以探索其他选择。

您可能会听到人们告诉您,在学习Common Lisp之前必须先学习Emacs。他们错了。您可以在任何熟悉的文本编辑器中开始学习justfine语言。

如果您没有偏好,则CCL本身与MacOS上的文本编辑器捆绑在一起。那将开始就好了。

Emacs,Vim,Sublime Text,Atom,无论如何,现在都无关紧要。只要它可以平衡括号,突出显示注释和字符串以及自动缩进Lispcode,那么您就需要开始所有这些操作。一旦您对语言更加满意,就不必担心要编辑编辑牛了。

要检查所有设置是否正确,请制作一个hello.lisp文件,其内容如下:

(defun hello()(写入行"您的名字是什么?")(let((名称(读取行))))(格式t" Hello,〜A.〜%& #34;名称)))

打开SBCL或CCL REPL(Read / Eval / PrintLoop)并通过输入(load" hello.lisp")来加载文件,然后调用该函数并确保其正常工作。如果选择了SBCL,它应该看起来像这样:

或者,如果您选择CCL但仍要使用命令行而不是MacOS应用程序(如果您使用64位系统,则命令行程序可能会令人讨厌地命名为ccl64):

如果您的箭头键和退格键在REPL中不起作用,请使用rlwrap修复该问题。 rlwrap sbcl将为您提供一个不惨的REPL。无论如何,rlwrap是一种方便的工具。

我发现在Common Lisp中入门的最好的书是Common Lisp:符号计算的温和介绍。这本书的确努力做到文雅。即使您在开始编程之前也仍然建议从这里开始,因为这样可以使您轻松使用该语言。

您可以从网站上免费获得1990年版,并且在2013年进行了重印,此版本修复了1990年版中的一些小错误。如果您买得起,我建议您购买2013年版,但1990年版也可以。

仔细阅读本书并进行所有练习。这将需要一段时间,并且主要是为了让您开始克服Common Lisp的一些主要障碍,例如:

如果您发现这本书移动得太慢,则向前略略浏览一下。略读是作为程序员练习的一项非常有用的技能。我认为作者最好在撰写书籍和文档时偏向于过多解释-如果您解释过多,专家读者应该习惯于略读,但是如果您重新解释,那么新用户将陷入困境创建新手的痛苦和困惑的小时数以节省一些专家滚轮的笔势,这是一个很差的折衷选择。

您还应该加入Freenode IRC网络上的#clschool频道,以便在遇到问题时可以提出问题。在大多数情况下,人们会友好而乐于助人,尽管我会提前警告您至少有一个人有时会磨砺。还有一个#clnoobs频道,但是在最近一波Freenode垃圾邮件浪潮中,这个频道几乎被放弃了,因为没有人愿意帮助打击垃圾邮件。

如果IRC并不是您的事,那么我们还有一些Discordserver,您可以在其中闲逛。加入那里的#common-lisp频道,我们将很乐意为您提供帮助。

完成那本书后,您应该攻击的下一本书是PracticalCommon Lisp。您可以根据需要获得纸质副本,但完整的书籍可在网站上免费获得。

您可以跳过编辑器/编程环境部分,因为它所建议的环境(盒中的Lisp)已被放弃并且不再起作用。暂时继续使用您熟悉的编程环境。

不幸的是,这本书没有练习。如果您确实想充分利用它,则可以在阅读并戳入密码时输入所有代码,但是如果您已经完成了上本书中的练习,则可能很安全只是坐下来仔细阅读这本书。每天不要阅读一两个以上的章节。您的大脑需要一些时间才能消化所有信息。

阅读本书时,请确保您理解所有内容。如果不清楚,请不要害怕在IRC或Discord上提问(如果需要,也可以给我发电子邮件)。

您还应该开始习惯于在Common Lisplanguage规范本身中查找内容。它是Common Lisp的终极手册。它可能有些密集,但是如果您仔细仔细地阅读它,可能会回答许多问题。您既可以使用索引页找到要查找的内容,也可以在Google上搜索“任何内容”。 (CLHS表示" Common Lisp HyperSpec&#34 ;,它是该规范的HTML版本的超链接)。如果您已经在MacOS上使用了Dash应用程序,则它具有Common Lispspec。

(有些人会告诉您只阅读规范即可学习语言。这太荒谬了-就像试图通过阅读字典来学习法语。这是一个有用的工具,但不是您只需要一个。)

一旦掌握了这两本书并进行了一些使用该规范的练习,就可以在没有人牵着手的情况下制作东西了。它不必

......