Ced*_*tin 9 lisp emacs scheme clojure common-lisp
无论Lisp方言如何,看起来每个包含Lisp函数的源代码文件本身都不是一个列表(我第一次"惊讶"这是在处理我的Emacs .el文件时).
我有几个问题,但它们都与同一个"问题"有关,而且可能只是我误解了一些问题.
有没有理由为什么各种Lisp方言的源代码文件似乎是一堆"无组织"的函数,如下所示:
(function1 ...)
(function2 ...)
(function3 ...)
Run Code Online (Sandbox Code Playgroud)
而不是函数的"Lisp列表",可能是这样的:
(
'(function1 ...)
'(function2 ...)
'(function3 ...)
)
Run Code Online (Sandbox Code Playgroud)
我有点惊讶这整个"代码是数据,数据是代码"的东西,看到源代码文件本身显然不是整齐的列表......或者是他们!?
源代码文件是否应该"操纵"?
如果我想将我的.clj(Clojure)源文件中的一个转换为某些CSS + HTML网页,那么源代码文件本身不是一个列表并不是一个"问题"吗?
我从Lisp开始,所以我不知道我的问题是否有意义,任何解释都是受欢迎的.
Rai*_*wig 12
在Common Lisp中,源文件包含lisp forms和注释.Lisp表单是数据或Lisp代码.源文件的典型操作由函数LOAD和COMPILE-FILE.
LOAD 将从文件中读取表单并逐个执行.
COMPILE-FILE更加复杂.它通常读取表单并将它们编译为其他表示形式(机器代码,字节代码,C代码......).它不执行代码.
如果文件包含一个表单列表而不是仅包含多个表单,那么它会对您有什么帮助?
现在举个例子,编译器会从文件流中读取lisp表单并逐个编译它们.
如果你想要所有表格,你可以做
CL-USER 170 > (defun read-forms (file)
(with-open-file (stream file)
(loop for form = (read stream nil nil)
while form
collect form)))
READ-FORMS
CL-USER 171 > (read-forms (capi:prompt-for-file "source file"))
((DEFPARAMETER *UNITS-TO-SHOW* 4.1)
(DEFPARAMETER *TEXT-WIDTH-IN-PICAS* 28.0)
(DEFPARAMETER *DEVICE-PIXELS-PER-INCH* 300)
(DEFPARAMETER *PIXELS-PER-UNIT* (* (/ (/ *TEXT-WIDTH-IN-PICAS* 6)
(* *UNITS-TO-SHOW* 2))
*DEVICE-PIXELS-PER-INCH*))
...
Run Code Online (Sandbox Code Playgroud)
如果你想围绕一切使用括号PROGN:
(progn
'form-1
(defun function-defintion-form () )
42)
Run Code Online (Sandbox Code Playgroud)
PROGN 保留其子表格的"顶级".
旁注:Lisp已经探索了几十年的替代方案.最突出的例子是现在已经不复存在的施乐公司的Interlisp-D.Interlisp-D是由Xerox PARC与Smalltalk并行开发的.Interlisp-D最初使用结构编辑器编辑Lisp数据,源代码被编辑为Lisp数据.开发环境基于这个想法.但从长远来看,"源文本"赢了.你仍然可以在许多当前的Lisp环境中模仿其中的一些.例如,许多Lisp系统允许写入当前执行存储器的"图像" - 该图像包括所有数据和所有代码(也包括编译代码).因此,您可以处理此数据/代码并不时保存图像.
Jer*_*emy 10
源代码文件只是存储列表的便利位置.Lisp代码(通常)旨在在read-eval-print-loop(REPL)中执行,其中每个输入本身就是一个列表.因此,当您执行源代码文件时,您可以想到它,因为它中的每个列表都被逐个读入REPL.我们的想法是,您拥有一个完全互动的环境,这个环境与"代码就是数据"范例相称.
当然,您可以将文件视为一个巨型列表,但是您暗示该文件具有明确定义的结构,但情况并非总是如此.如果你真的想要创建一个包含一个巨大列表的文件,那么没有什么能阻止你这样做.您可以使用Lisp阅读器将其作为一个大型列表(数据?)读取并根据需要对其进行处理(可能使用某种eval?).以Leiningen的project.clj文件为例.它们通常只是一个很大的反对项目清单.
要彻底,所有源文件都是文本,而不是lisp数据结构.要评估或编译代码,lisp必须首先READ是文件,这意味着将文本转换为lisp数据结构.回想一下首字母缩略词REPL,前两个字母代表READ,和EVAL.READ获取代码的字符串表示形式,并返回表示代码的数据结构.EVAL获取返回的数据结构,并将数据结构解释(或编译并运行)为代码.因此,重要的是要记住涉及中间步骤.
一个很好的问题是,当READ你提到多个s表达式时,会发生什么,并且它们不在列表中?
如果你查看代码,你通常会发现多个版本READ,clojure read-string只读取并返回第一个s表达式,忽略其余的.但是,在clojure中使用的读者load-file将采用整个字符串,并且"有效地"(实现可能不同)围绕所有表单包装隐式do(或progn通常的lisp),然后将其传递给eval.此行为与REPL中发生的情况形成对比,表单按顺序读取,评估和打印.
但在这两种情况下,这种"幕后"行为都是为了简洁而进行的权衡.我们可以假设当我们加载s表达式的文本文件时,我们希望它们都被评估,并且最多返回最后一个s-expression的值.
在Lisp中有两级源代码,或根本没有源代码,具体取决于您如何定义源代码.
这两个级别是存在的,因为Lisp解释器/编译器(通常)执行两个单独的概念步骤.
在此步骤中,源代码是一系列字符,例如来自文件.这里括号,引用的字符串,数字,符号,引号,甚至部分准语法都被处理并转换为Lisp数据结构.在此级别,语法规则是关于括号,数字,管道,引号,分号,尖锐符号,逗号,符号等.
在此步骤中,输入是Lisp数据结构,输出是机器代码,字节代码,或者源可能由解释器直接执行.在这个级别的语法是关于特殊形式......如含义(if ...),(labels ...),(symbol-macrolet ...)等等.Lisp代码中的结构是统一的(只是列表和原子),但语义不是(if表单看起来像函数调用,但它们不是).
因此,在这种观点中,您的答案的问题是肯定而不是.对于步骤1是否,对于步骤2是.如果您仅考虑文件,则答案为否...文件包含字符,而不是列表.这些字符可以由读者转换为列表.
为什么然后有人说Lisp没有语法,实际上有两种不同的语法级别?原因是这两个级别都在程序员的控制之下.
您可以通过定义读取器宏来自定义级别1,并且可以通过定义宏来自定义级别2.因此Lisp没有固定的语法,因此源文件可以以"lispy"外观开始,并且可以看起来与Python代码完全相同.
源文件可以包含任何内容(从某一点开始),因为初始表单可以定义一些新的阅读规则,这些规则将改变后续字符的含义.
通常Lisp程序员不会在读取级别上做疯狂的事情,因此大多数Lisp源代码文件看起来就像Lisp表单的序列,并且它们仍然是"lispy".
但这不是一个严格的约束......例如,我并不是在开玩笑说Lisp语法变成了Python:有人这样做了.