newLISP 简介/基础
安装说明可在 newlisp.org 找到。
安装 newLISP 后,有各种方法可以运行它。有关详细信息,请参阅 newLISP 文档。最直接的方法是通过在控制台或终端窗口中键入 newlisp 命令来从命令行运行 newLISP 解释器。
$ newlisp newLISP v.x on OSX IPv4 UTF-8, execute 'newlisp -h' for more info. >
这对于尝试简短的表达式、测试想法和调试非常有用。您可以在此环境中编写多行代码,方法是在 [cmd] 和 [/cmd] 之间包含这些行。在新版本中,您只需在空白行上使用回车键即可开始和结束多行块。
>[cmd] (define (fibonacci n) (if (< n 2) 1 (+ (fibonacci (- n 1)) (fibonacci (- n 2))))) [/cmd] >(fibonacci 10) 89 >
newLISP 的可视化界面 newLISP-GS 为 newLISP 应用程序提供了图形工具包,它还为您提供了用于编写和测试代码的开发环境:newLISP 编辑器。在 Windows 上,它以桌面图标和程序开始菜单中的文件夹的形式安装。在 MacOS X 上,应用程序包和图标安装在应用程序文件夹中。newLISP-GS 编辑器为您提供多个带标签的窗口、语法着色以及一个用于查看运行代码结果的监视区域。
您还可以从命令行运行 newLISP-GS 编辑器:您可以在 C:/Program Files/newlisp/newlisp-edit(Windows)或 /usr/bin/newlisp-edit(在 Unix 上)找到该文件。(这是一个 newLISP 源文件,因此您也可以查看代码。)
您可以在自己喜欢的文本编辑器中编辑 newLISP 脚本。在 MacOS X 上,您可以使用 BBEdit、TextWrangler 或 TextMate 运行 newLISP 脚本,或者您可以使用预安装的 Unix 文本编辑器,例如 vim 或 emacs。在 Windows 上,您可以使用 UltraEdit、EditPlus 或 NotePad++,仅举几例。如果您使用 Linux,您比我更了解文本编辑器,您可能已经有自己的偏好。
newLISP 网站在 http://newlisp.org/index.cgi?Code_Contributions 主持多个流行编辑器的配置文件。
在 Unix 上,newLISP 脚本的第一行应该是
#!/usr/local/bin/newlisp
或者
#!/usr/bin/env newlisp
如果您更喜欢在外部文本编辑器中运行 newLISP,则必须使用更多 println 函数来查看每个函数或表达式返回的值。
通常,您使用 exit 函数结束 newLISP 脚本或控制台会话
(exit)
您只需要学习三个基本规则来使用 newLISP 进行编程。以下是第一个规则
列表是被括号括起来的元素序列
(1 2 3 4 5) ; a list of integers
("the" "cat" "sat") ; a list of strings
(x y z foo bar) ; a list of symbol names
(sin cos tan atan) ; a list of newLISP functions
(1 2 "stitch" x sin) ; a mixed list
(1 2 (1 2 3) 3 4 ) ; a list with a list inside it
((1 2) (3 4) (5 6)) ; a list of lists
列表是 newLISP 中的基本数据结构,也是您编写程序代码的方式。但现在还不要输入这些示例——还有两个规则要学习!
当 newLISP 看到一个列表时,它会将第一个元素视为一个函数,然后尝试使用其余元素作为该函数所需的信息。
(+ 2 2)
这是一个包含三个元素的列表:名为 + 的函数,后面跟着两个数字。当 newLISP 看到这个列表时,它会对其进行评估并返回 4(当然)的值。请注意,第一个元素被 newLISP 视为一个函数,而其余元素被解释为该函数的参数——函数期望的数字。
以下是一些更进一步的示例,说明了这两个基本规则
(+ 1 2 3 4 5 6 7 8 9)
返回 45。+ 函数将列表中的所有数字加起来。
(max 1 1.2 12.1 12.2 1.3 1.2 12.3)
返回 12.3,即列表中最大的数字。同样,列表的长度没有(合理的)限制:如果一个函数乐于接受 137 个项目(max 和 + 都可以),那么您可以传递 137 个项目。
(print "the sun has put his hat on")
"the sun has put his hat on"
打印字符字符串 the sun has put his hat on。(它还会返回字符串,这就是为什么当您在控制台中工作时,有时会看到事物重复出现两次。)print 函数可以打印单个字符字符串,或者您可以提供要打印的元素序列
(print 1 2 "buckle" "my" "shoe")
12bucklemyshoe
它会打印两个数字和三个字符串(虽然格式不太好,因为您还没有接触到 format 函数)。
directory 函数
(directory "/")
生成指定目录(在本例中为根目录 "/")的列表
("." ".." ".DS_Store" ".hotfiles.btree" ".Spotlight-V100"
".Trashes"".vol" ".VolumeIcon.icns" "Applications"
"automount" "bin" "cores" "Desktop DB" "Desktop DF"
"Desktop Folder" "dev""Developer" "etc" "Library"
"mach" "mach.sym" "mach_kernel" "Network" "private"
"sbin" "System" "System Folder" "TheVolumeSettingsFolder"
"tmp" "User Guides And Information" "Users" "usr"
"var" "Volumes")
如果您没有指定目录,它会列出当前目录
(directory)
("." ".." "2008-calendar.lsp" "allelements.lsp" "ansi.lsp"
"binary-clock.lsp" ... )
有一个 read-file 函数可以读取文本文件的内容
(read-file "/usr/share/newlisp/modules/stat.lsp")
这里,该函数需要一个参数——文件名——并将文件的内容以字符串形式返回给您。
这些是 newLISP 代码构建块的典型示例——包含函数调用的列表,后面可能还有该函数需要的任何额外信息。newLISP 有 380 多个函数,您可以参考出色的 newLISP 参考手册以了解所有函数的详细信息以及如何使用它们。
您可以尝试这些示例。如果您在终端中使用 newLISP,只需输入它们即可。如果您将这些行输入文本编辑器并将其作为脚本运行,除非您将表达式包含在 println 函数中,否则您可能不会看到函数调用的结果。例如,键入
(println (read-file "/usr/share/newlisp/modules/stat.lsp"))
以打印 read-file 函数的结果。
每个 newLISP 表达式都会返回一个值。即使是 println 函数也会返回一个值。您可以说,打印操作实际上只是一个副作用,其主要任务是返回一个值。您可能会注意到,当您在控制台窗口中以交互方式使用 println 时,您会看到返回值出现两次:一次是打印时,一次是返回值返回到调用函数(在本例中为顶层)时。
在您学习第三条规则之前,还有一件有用的事情需要查看。
您已经发现一个列表嵌套在另一个列表中。以下是一个示例
(* (+ 1 2) (+ 3 4))
当 newLISP 看到这个时,它会思考如下
嗯。让我们从那些内部列表中的第一个开始。我可以轻松地做到
(+ 1 2)
它的值是 3。我也可以轻松地做到第二个列表
(+ 3 4)
足够简单。它评估为 7。
因此,如果我将这两个内部列表替换为这些值,我将得到
(* 3 7)
这真的很容易。我将为这个表达式返回 21 的值。
(* (+ 1 2) (+ 3 4))
(* 3 (+ 3 4))
(* 3 7)
21
看到第一行末尾的两个右括号吗?它们都是必要的:第一个括号结束 (+ 3 4 列表,第二个括号结束以 (* 开头的乘法运算。当您开始编写更复杂的代码时,您会发现自己将列表放入列表中,再放入列表中,再放入列表中,并且您可能会以六个右括号来结束一些更复杂的定义。一个好的编辑器会帮助您跟踪它们。
但是您不必担心空格、行终止符、各种标点符号或强制缩进。并且由于所有数据和代码都以相同的方式(在列表中)存储,因此您可以自由地混合它们。稍后会详细介绍。
有些人第一次看到 LISP 代码时会担心括号泛滥。其他人称它们为指甲碎片,或者说 LISP 代表大量的令人讨厌的愚蠢括号。但我更喜欢将括号视为包围 newLISP 思想的小把手
当您在好的编辑器中编辑 newLISP 代码时,您可以通过抓住其句柄轻松地移动或编辑一个想法,并使用“平衡括号”命令轻松地选择一个想法。您很快就会发现括号比您最初认为的更有用!
现在您可以使用 newLISP 了解编程的第三条规则
要停止 newLISP 评估某个内容,请引用它。
比较这两行
(+ 2 2)
'(+ 2 2)
第一行是一个列表,包含一个函数和两个数字。在第二行中,该列表被引用 - 在前面有一个单引号或撇号 (')。您不需要在结束括号后添加另一个引号,因为一个就足够了。
> (+ 2 2)
4
> '(+ 2 2)
(+ 2 2)
>
对于第一个表达式,newLISP 照常执行其工作,并热心地评估该列表,返回数字 4。但对于第二个表达式,newLISP 一看到引号,就不会考虑通过添加数字来评估该列表;它只返回该列表,不进行评估。
此引号在 newLISP 中的作用与书面英语中的开闭引号相同 - 它们告诉读者该单词或短语不要按通常方式解释,而要以某种特殊方式对待:非标准或讽刺的含义,也许是另一个人说的话,或者是不应该照字面理解的东西。
那么,为什么您要阻止 newLISP 评估事物呢?您很快就会遇到一些示例,其中您引用事物以防止 newLISP 认为列表中的第一个项目是一个函数。例如,当您将信息存储在列表中时,您不希望 newLISP 以通常的方式评估它们
(2006 1 12) ; today's year/month/date
("Arthur" "J" "Chopin") ; someone's full name
您不希望 newLISP 寻找名为2006或"Arthur"的函数。此外,2006不是有效的函数名,因为它以数字开头,函数名不能以双引号开头,因此在任何情况下您的程序都会停止并出现错误。因此,您需要引用列表以阻止其第一个元素用作函数而不是数据
'(2006 1 12) ; evaluates to (2006 1 12)
'("Arthur" "J" "Chopin") ; evaluates to ("Arthur" "J" "Chopin")
newLISP 将表达式视为数据 - 以及将数据视为表达式 - 的能力将在后面详细讨论。
使用垂直撇号(ASCII 码 39)来引用列表和符号。有时,文本编辑器或其他程序会将这些简单的垂直撇号更改为花括号。它们的作用不同,因此您必须将所有智能引号更改为垂直撇号。
符号是带有名称的 newLISP事物。您在代码中定义了一些东西并为其分配一个名称。然后,您可以在以后使用该名称而不是内容来引用该东西。例如,在键入以下内容后
(set 'alphabet "abcdefghijklmnopqrstuvwxyz")
现在有一个名为alphabet的新符号,其值为一个字符串,该字符串包含字母表的 26 个字母。set函数将从 a 到 z 的字符字符串存储在alphabet符号中。现在,此符号可以在其他地方使用,并在使用时始终评估为字母表。每当您想要使用字母表的 26 个字母时,您都会使用此符号,而不会引用它。例如,以下是upper-case函数
(upper-case alphabet)
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
我使用该符号而不引用它,因为我想要 newLISP 使用该符号的值,而不是其名称。我实际上对将单词alphabet转换为大写不感兴趣,而是对字母表本身感兴趣。在本例中,newLISP 没有永久更改符号的值,因为upper-case始终创建并返回一个新字符串,使存储在符号中的字符串保持不变。
符号对应于其他编程语言中的变量。实际上,newLISP 使用符号的频率并不像其他语言使用变量那样高。这部分是因为值会不断地由表达式返回并直接馈送到其他表达式,而无需存储。例如,在以下代码中,每个函数都会将其结果直接传递给下一个包含函数
(println (first (upper-case alphabet)))
"A"
upper-case将其返回值直接传递给first,first将其返回值直接传递给println,println既打印它,也为您提供它打印的字符串作为返回值。因此,没有必要临时存储值。但还有很多其他地方您确实需要符号。
以下是符号引用的另外两个示例
(define x (+ 2 2 ))
(define y '(+ 2 2))
在第一个示例中,我没有引用(+ 2 2)列表 - newLISP 将其评估为 4,然后将 4 分配给符号x,它评估为 4
x
;-> 4
在第二个示例中,我引用了该列表。这意味着符号y现在保存的是一个列表,而不是一个数字。每当 newLISP 看到符号y时,它都会返回该列表,而不是 4。(当然,除非您也先引用y!)
y
;-> (+ 2 2)
'y
;-> y
顺便说一下,在本文档中
; the semicolon is the comment character
;-> that ";->" is my way of saying "the value is"
以及 newLISP 解释器打印的输出通常显示为
like this
创建和设置符号值的方法有很多。您可以使用define或set,如下所示
(set 'x (+ 2 2))
;-> 4
(define y (+ 2 2))
;-> 4
set期望后面跟着一个符号,但首先会评估其第一个参数。因此,您应该引用一个符号以防止它被评估(因为它可能评估为除符号以外的其他东西),或者提供一个评估为符号的表达式。define不期望参数被引用。
您还可以使用setf和setq来设置符号的值。这些期望第一个参数为符号或符号引用,因此您不必引用它。
(setf y (+ 2 2))
;-> 4
(setq y (+ 2 2))
;-> 4
这两个函数(它们具有相同的操作)可以设置符号(变量)、列表、数组或字符串的内容。一个约定是在设置符号时使用setq,在设置列表或数组的元素时使用setf。
define还用于定义函数。参见创建您自己的函数.
一些 newLISP 函数会修改它们操作的符号的值,而另一些则会创建值的副本并返回该副本。从技术上讲,修改符号内容的函数被称为破坏性函数 - 尽管您通常会使用它们来创建新数据。在本文件中,我将描述诸如push和replace之类的函数为破坏性函数。这仅仅意味着它们会改变某个东西的值,而不是返回一个修改后的副本。