使用 Cusp Eclipse 插件开发 Lisp 应用程序
使用 Cusp Eclipse 插件开发 Lisp 应用程序
Lisp 不再只适用于人工智能 —— 试试它,您可能会用不同的方式思考 Java 和 C 编程
Cusp 是 Common Lisp 编程语言的开发环境。使用 Lisp 可以开发所有类型的应用程序,包括 Web 应用程序。Lisp 是目前仍在使用的第二种最古老的编程语言(在 Fortran 之后)并被称为第一种函数语言。Lisp 的创建开始于二十世纪五十年代后期,并且于 1958 年由 MIT 人工智能 (AI) 项目首次实现。它的强大之处在于处理列表:AI 和符号数学。实际上,Lisp 是 “list processor” 的简写,正是出于对列表处理器的需求创建了这种编程语言,您将在稍后的段落中看到相关说明(有关 Lisp 的历史信息,请参阅 参考资料 )。
您将会发现 Lisp 与其他普通的编程语言根本不同。例如,在大多数普通编程语言中,执行乘法就像您在纸上作运算一样:int times = 5 * 5
;。
使用 Lisp,将通过代码 (* 5 5 3)
得到 75。要获得此列表中的最大值,使用 (MAX 9 8 7 6 20)
将返回 20。
注意,这里所说的 “第一种函数语言” 十分贴切,因为一切都基于函数。每个函数都可以有若干数目不定的参数。使用递归和诸如 car
和 cdr
之类的 Lisp 函数处理这些列表是使用 Lisp 处理列表的强大之处。
由于 Lisp 历史较久,因此您可以找到许多适用于 Lisp 的开发环境,但是,也存在旧编程语言固有的问题,工具支持大部分都是基于文本的,并且对于新手来说不够直观。因此,尝试使用 Cusp 的一个原因是它提供了供新手了解和开发 Lisp 的直观 GUI,而不使用基于文本的 “尝试记住所有这些命令” 类型的界面。它还比 Eclipse GUI 框架固有的 superb 项目管理功能更具优势。
继续之前,您需要从 Eclipse.org 下载最新的 Eclipse 标准包。然后了解如何安装和设置 Cusp。
现在应当有一个经过压缩的 Eclipse 标准包归档。请将其解压缩并运行 eclipse.exe。
要获得最新版本的 Cusp,请单击 Help > Software Updates > Find and Install
。单击 Search
for new features to install
选项。现在单击 New Remote Site
按钮。键入名称 Cusp update site
和 URL http://www.sergeykolos.com/cusp/update
,然后单击 OK
(参见图 1)。
单击已添加的新远程站点旁边的复选框并单击 Finish (参见图 2)。
图 2. 选择 Cusp Eclipse Update Site
系统将显示另一个窗口,在该窗口中需要展开 Cusp Eclipse Update Site 并选择 Cusp,如下所示:
单击 Next 。然后阅读许可证协议并单击 I accept the terms in the license agreement 接受许可证协议,然后单击 Next 。确保选中所有库,如下所示,然后再次单击 Next 。
最后一个页面将显示要安装功能的安装汇总。单击 Finish 。插件及其组件将下载并安装。下载后,您将收到一则警告,表明将要安装一个未签名的功能。单击 Install all 。在安装结束时,系统将询问您是否要重新启动 Eclipse;请单击 Yes 。
您已经安装了 Eclipse Cusp 插件。接下来,将创建一个 Lisp 项目。
在开始进行 Lisp 开发之前,必须先创建一个新的 Lisp 项目。您可以通过转到 File > New > Project 来完成此操作。展开 Lisp 文件夹,单击 Lisp Project ,然后单击 Next 。为新项目选择一个名称 my_new_lisp_project,如下所示,然后单击 Finish 。
单击 Finish 后,将创建新项目并打开 Lisp 透视图,如下所示:
查看 Lisp 透视图。Lisp Navigator 窗口显示打开的项目及其相关文件。Outline 窗口显示当前打开文件的概要。位于右上侧显示 main.lisp 的窗口是 Lisp 开发窗口。位于右下侧的窗口 REPL 是 Lisp 的命令行 Lisp 解析程序,您可以在这里运行 Lisp 命令。
如果关闭并重新打开 Eclipse,则需要装入项目的 .asd 文件,如下所示:
注意,需要在 my_new_lisp_project 文件夹下的 my_new_lisp_project.asd 文件上右键单击,然后选择 Load asd 。这实际上就是在 REPL 窗口中编译 Lisp 项目,允许您输入可以使用新代码的 Lisp 命令。
接下来,将尝试使用 Cusp 进行一些 Lisp 开发。
首先,还是定义并测试一个简单的自定义函数。打开 main.lisp 文件,并使用 defun
(定义函数)命令,然后添加以下内容:
... (defun first_howdy_function () "howdy ho") |
保存该文件。要从包中导出函数,请在 defpackage.lisp 中键入以下代码:
... ;; Exported symbols go here first_howdy_function )) |
可以从包的外部使用该函数。要测试新函数,请在 REPL 中位置靠下的窗口中键入以下代码:(my_new_lisp_project:first_howdy_function)
。
注意,在您所处的范围内,这样做与输入 (my_new_lisp_project::first_howdy_function)
等效。如果未在 defpackage.lisp 中导出函数,则这是您必须键入的代码。
单击 Send ,然后查看输出。输入上面任意一条命令所得到的输出都是:
COMMON-LISP-USER> (my_new_lisp_project:first_howdy_function) "howdy ho" |
您已经得到它了:第一个 Lisp Howdy 函数。
尝试为 echo
函数提供一个输入:
... (defun first_echo_function (echoval) echoval) |
同上一个函数一样,将其导出到 defpackage.lisp 中。在 REPL 窗口中位置靠下的部分中键入以下代码 (my_new_lisp_project:first_echo_function '("howd" "y h" "o"))
来测试这个 first_echo_function
。注意,'("howd" "y h" "o")
部分是用于定义列表的语法。首先,单引号必须放在括号之前,然后在括号内定义列表元素。输出如下所示:
COMMON-LISP-USER> (my_new_lisp_project:first_echo_function '("howd" "y h" "o")) ("howd" "y h" "o") |
您将创建一种分别处理每个列表元素的方法,这是 Lisp 的真正强大之处。定义如下:
(defmethod concat2 ((str1 string) (str2 string)) (concatenate 'string str1 str2)) |
注意,以上方法实际上定义了一个类型字符串。到现在为止,您一直在使用 Lisp 作为一种大型的无类型语言。虽然双引号隐式地把数据类型定义为字符串,但是以上方法显式地把 concat2
函数的输入和输出类型定义为字符串。此方法还使用内置的 concatenate 函数把两个字符串组合在一起,并将其作为单个字符串返回。
要测试 concat2
,请将其导出,然后键入 (my_new_lisp_project:concat2 "howd" "y ho")
。输出如下:
COMMON-LISP-USER> (my_new_lisp_project:concat2 "howd" "y ho") "howdy ho" |
完成。字符串 "howd"
和 "y ho"
变成 "howdy ho"
。现在您将使用两个著名的 Lisp car
和 cdr
函数创建更通用化的 concatenation 函数。
(defun concat3 (args_list) (concat2 (car args_list) (concat2 (car (cdr args_list)) (car (cdr (cdr args_list)))))) |
注,此函数仍使用 concat2
函数,但是使用参数列表作为输入。注意如何从 args_list
检索连接的各个部分。car
将从列表中获取第一个元素。cdr
将返回减去第一个元素的列表。只需对列表调用 car
函数就可以看到如何获得第一个元素。获得第二个元素要求对列表调用 cdr
,然后对新列表调用 car
。通过对列表两次调用 cdr
并对得到的列表调用 car
获取第三个元素。
导出后以上函数的输出为:
COMMON-LISP-USER> (my_new_lisp_project:concat3 '("howd" "y h" "o")) "howdy ho" |
就是这样。已正确连接了三个字符串来生成 "howdy ho"
。接下来,创建递归函数。
将要创建的最后一个函数要执行一些递归操作,这是使用 Lisp 进行列表处理的真正强大之处。迭代是可能实现的(逐个遍历所有条目),但是不同于诸如 Java 语言之类的普通语言,递归是到现在为止 Lisp 中处理列表的最简单方法。在本节的末尾,您将明确了解递归的含义。
首先创建递归的 concat
函数。
1 (defun concat_recursive (args_list) 2 (if (equal (cdr args_list) nil) 3 (car args_list) 4 (concat2 (car args_list) 5 (concat_recursive (cdr args_list))))) |
递归是十分难于处理的概念,因此让我们一起来查看这个概念:
- 假定将任意参数列表传递给上面的函数。
- 如果列表中只有一个元素(第 2 行中的
(cdr args_list)
部分返回 nil),则返回单个元素(第 3 行中的(car args_list)
)。 - 如果列表中有多个元素(意味着第 2 行中的
(cdr args_list)
部分未返回 nil),则返回使用(cdr args_list)
的结果作为参数(参见第 5 行)连接(使用concat2
)列表的第一个元素(参见第 4 行)与递归调用concat_recursive
的结果的结果。
当传递列表 '("ho" "wd" "y" "h" "o")
作为参数时,下面是递归输出:
- 第一次执行到第 2 行时,
if
语句为 false,并用"ho"
和(concat_recursive '("wd" "y" " h" "o"))
调用concat2
。 - 第二次执行到第 2 行时,
if
语句又为 false,并用"wd"
和(concat_recursive '("y" " h" "o"))
调用concat2
。 - 第三次执行到第 2 行时,
if
语句又为 false,并用"y"
和(concat_recursive '(" h" "o"))
调用concat2
。 - 第四次执行到第 2 行时,
if
语句又为 false,并用" h"
和(concat_recursive '("o"))
调用concat2
。 - 第五次递归结束。这是因为这一次,第 2 行中的
if
语句现在为 true,并且简单地返回了"o"
。递归解开为:- 第四次:连接并返回
"h"
和"o"
。 - 第三次:连接并返回
"y"
和"ho"
。 - 第二次:连接并返回
"wd"
和"y ho"
。 - 第一次:连接并返回
"ho"
和"wdy ho"
作为最终结果。
- 第四次:连接并返回
这时就完成了递归 —— "howdy ho" 最终被返回,如下所示:
COMMON-LISP-USER> (my_new_lisp_project:concat_recursive '("ho" "wd" "y" " h" "o")) "howdy ho" |
已经将递归添加到了 Cusp 开发工具库中。接下来请尝试调试器。
考虑输入条件可能会导致 concat_recursive
失败,因此调试器可能会派得上用场。一种情况是可以发送一个混有数字的字符串来查看会发生什么情况。记住,使用 concat2
进行连接要求使用两个字符串,因此数字将导致此函数在递归之间中断。
键入以下命令:(my_new_lisp_project:concat_recursive
'("ho" "wd" "y" 55 " h" "o"))
。注意没有用双引号引起的数字 55 不是字符串并将导致显示调试器,如下所示:
注意,触发调试器的主要错误是无法对列表 (55 "
ho")
调用 concat2
。另请注意,在触发该错误之前已经连接了 "h"
和 "o"
。
在调试器窗口中,您还可以看到 ==Backtrace==
,如图 8 所示。调试器下部的每行(本例中为 0 至 19 行)给出了从单击 Send
到发生错误的详细跟踪。在这里,您还可以查看并跟踪在触发了错误的数字 55 之前的递归。
现在做什么?遇到上面的情况,您有三种选择退出调试器并修改和验证函数输入:可以中止命令并返回到正常的 REPL 窗口;可以关闭连接(通过测试发现,如果选择此选项,则需要重新引导 Eclipse 来重新启动 Lisp 处理器);可以简单地中止调试器并返回到正常的 REPL 窗口。退出调试器的最佳选项始终是中止命令并返回到正常的 REPL 窗口。
就是这样。您已经在 Lisp 中实现了递归函数。
您已经成功地使用 Cusp Eclipse 插件完成了 Lisp 开发。现在应当知道 Lisp 为什么如此强大。通过简单的递归语句,可以轻松地推动处理符号和数据列表。Cusp 为 Lisp 的功能补充了内置 Cusp 调试器、拥有项目管理功能的可靠 GUI、交互式 Lisp 编辑器以及可以输入命令和测试代码的 Lisp 处理器命令行接口。要获得更多信息,请一定要查看 参考资料 。