我可以在Clisp中保存源文件吗?

我可以在Clisp中保存源文件吗?,lisp,common-lisp,clisp,land-of-lisp,Lisp,Common Lisp,Clisp,Land Of Lisp,我是一名初级程序员,正在阅读《Lisp之地》一书 我一直在用REPL输入书中的示例。是否可以将当前程序保存为.lisp文件,以便我可以加载它并在以后继续使用?我知道我可以在文本编辑器中创建.lisp文件并将其加载,但我很喜欢在全屏模式下使用REPL来完成示例。Short answer 不可以。一旦你在REPL中输入了一个函数,源表单就消失了,你只需要解释或编译表单。你可以做一些聪明的事情,但我怀疑你现在不想处理它们 长话短说 使用Emacs和SLIME 首先,我知道您很喜欢REPL,但我鼓励您查

我是一名初级程序员,正在阅读《Lisp之地》一书

我一直在用REPL输入书中的示例。是否可以将当前程序保存为.lisp文件,以便我可以加载它并在以后继续使用?我知道我可以在文本编辑器中创建.lisp文件并将其加载,但我很喜欢在全屏模式下使用REPL来完成示例。

Short answer 不可以。一旦你在REPL中输入了一个函数,源表单就消失了,你只需要解释或编译表单。你可以做一些聪明的事情,但我怀疑你现在不想处理它们

长话短说 使用Emacs和SLIME 首先,我知道您很喜欢REPL,但我鼓励您查看一些支持Lisp的编辑器,例如使用SLIME()的Emacs,它提供了两个方面的最佳功能。你在编辑器中输入,它在REPL中输入,你真的不知道与你现在做的事情有什么不同。然后,您可以将喜欢的函数复制并粘贴到一个“正确的”.lisp文件中。使用Emacs的另一个优点是,它基于称为Elisp的Lisp变体,因此您可以用Lisp编写编辑器。您可以漂亮地打印代码,重新格式化并将其重构为多个函数,还可以完成各种出色的工作

说教够了! 如果您仍然只想键入clisp并在REPL上播放,那么您仍然有选项

如果您想记录REPL会话的输出,请查看运球。它会将您的会话记录到一个文件中,您可以稍后编辑该文件以提取所需内容

例如,这是一个简单的会话:

ataylor:~  $ clisp
ataylor:~  $ clisp
废话

[1]> (dribble "/Users/ataylor/jerome.lisp")
#<OUTPUT BUFFERED FILE-STREAM CHARACTER #P"/Users/ataylor/jerome.lisp">
[2]> (defun add-two (a b) (+ a b))
ADD-TWO
[3]> (add-two 1 2)
3
[4]> (dribble)
#<CLOSED OUTPUT BUFFERED FILE-STREAM CHARACTER #P"/Users/ataylor/jerome.lisp">
[5]> 
Bye.
[1]> (load "/Users/ataylor/jerome1.lisp")
;; Loading file /Users/ataylor/jerome1.lisp ...
;; Loaded file /Users/ataylor/jerome1.lisp
T
[2]> (add-two 1 2)
3
[3]> 
Bye.
[1]> (defun add-two (a b) (+ a b))
ADD-TWO
[2]> (add-two 1 2)
3
[3]> (EXT:SAVEINITMEM)
;; Wrote the memory image into lispinit.mem (3,422,616 bytes)
Bytes permanently allocated:            171,840
Bytes currently in use:               3,243,400
Bytes available until next GC:          808,130
3243400 ;
808130 ;
171840 ;
1 ;
65640 ;
7834
[4]> 
Bye.
[1]> (add-two 1 2)
3
废话

[1]> (dribble "/Users/ataylor/jerome.lisp")
#<OUTPUT BUFFERED FILE-STREAM CHARACTER #P"/Users/ataylor/jerome.lisp">
[2]> (defun add-two (a b) (+ a b))
ADD-TWO
[3]> (add-two 1 2)
3
[4]> (dribble)
#<CLOSED OUTPUT BUFFERED FILE-STREAM CHARACTER #P"/Users/ataylor/jerome.lisp">
[5]> 
Bye.
[1]> (load "/Users/ataylor/jerome1.lisp")
;; Loading file /Users/ataylor/jerome1.lisp ...
;; Loaded file /Users/ataylor/jerome1.lisp
T
[2]> (add-two 1 2)
3
[3]> 
Bye.
[1]> (defun add-two (a b) (+ a b))
ADD-TWO
[2]> (add-two 1 2)
3
[3]> (EXT:SAVEINITMEM)
;; Wrote the memory image into lispinit.mem (3,422,616 bytes)
Bytes permanently allocated:            171,840
Bytes currently in use:               3,243,400
Bytes available until next GC:          808,130
3243400 ;
808130 ;
171840 ;
1 ;
65640 ;
7834
[4]> 
Bye.
[1]> (add-two 1 2)
3
保存会话 最简单的方法是将Lisp会话保存到图像中。它将保存您创建或编译的所有函数以及大多数状态。当您在下一个会话中加载它时,它几乎就像您没有退出clisp一样。实现这一点的方法取决于实现,并且在clisp、sbcl等之间有所不同。我将向您展示如何处理clisp

这样做的问题是,您无法打开文件并对其进行编辑、将其发布到github或其他任何地方。我将举一个简单的例子

废话

[1]> (dribble "/Users/ataylor/jerome.lisp")
#<OUTPUT BUFFERED FILE-STREAM CHARACTER #P"/Users/ataylor/jerome.lisp">
[2]> (defun add-two (a b) (+ a b))
ADD-TWO
[3]> (add-two 1 2)
3
[4]> (dribble)
#<CLOSED OUTPUT BUFFERED FILE-STREAM CHARACTER #P"/Users/ataylor/jerome.lisp">
[5]> 
Bye.
[1]> (load "/Users/ataylor/jerome1.lisp")
;; Loading file /Users/ataylor/jerome1.lisp ...
;; Loaded file /Users/ataylor/jerome1.lisp
T
[2]> (add-two 1 2)
3
[3]> 
Bye.
[1]> (defun add-two (a b) (+ a b))
ADD-TWO
[2]> (add-two 1 2)
3
[3]> (EXT:SAVEINITMEM)
;; Wrote the memory image into lispinit.mem (3,422,616 bytes)
Bytes permanently allocated:            171,840
Bytes currently in use:               3,243,400
Bytes available until next GC:          808,130
3243400 ;
808130 ;
171840 ;
1 ;
65640 ;
7834
[4]> 
Bye.
[1]> (add-two 1 2)
3
请注意,关于clisp写入内存映像的位置的消息。下次启动时,您将使用
-M
标志将其返回给clisp

ataylor:~  $ clisp -M lispinit.mem 
废话

[1]> (dribble "/Users/ataylor/jerome.lisp")
#<OUTPUT BUFFERED FILE-STREAM CHARACTER #P"/Users/ataylor/jerome.lisp">
[2]> (defun add-two (a b) (+ a b))
ADD-TWO
[3]> (add-two 1 2)
3
[4]> (dribble)
#<CLOSED OUTPUT BUFFERED FILE-STREAM CHARACTER #P"/Users/ataylor/jerome.lisp">
[5]> 
Bye.
[1]> (load "/Users/ataylor/jerome1.lisp")
;; Loading file /Users/ataylor/jerome1.lisp ...
;; Loaded file /Users/ataylor/jerome1.lisp
T
[2]> (add-two 1 2)
3
[3]> 
Bye.
[1]> (defun add-two (a b) (+ a b))
ADD-TWO
[2]> (add-two 1 2)
3
[3]> (EXT:SAVEINITMEM)
;; Wrote the memory image into lispinit.mem (3,422,616 bytes)
Bytes permanently allocated:            171,840
Bytes currently in use:               3,243,400
Bytes available until next GC:          808,130
3243400 ;
808130 ;
171840 ;
1 ;
65640 ;
7834
[4]> 
Bye.
[1]> (add-two 1 2)
3

出于以下原因,我希望对同一问题有一个更好的答案:

(1) 在学习一门语言时,通常您更希望进行实验,而不是实际编写完整的应用程序

(2) 在进行实验时,查看您的历史记录并查看您输入的内容和得到的结果是非常好的

(3) 我来自
bash
的世界,在那里你可以将你的命令历史记录转储到一个文本文件中,并称之为“脚本”(虽然这只是你通过
bash
脚本所能获得的能力的一小部分,但它仍然可以达到事后自动化的目的,只需稍加清理)

因此,从
bash
的世界中,我只使用了简单的方法——我自动清理stdout转储。如果您正在使用具有回滚历史记录的终端(虚拟终端除外,如果您在VT中编写lisp代码,您会觉得很奇怪),以交互方式玩
clisp
,只需将整个终端回滚历史记录复制到一个文件中,然后运行:

sed -n -e 's/^\[[0-9]\+\]> //;tstuff' -e 'b;:stuff' -e 'p;:rep' -e 's/([^()]*)//;trep' -e '/^[^(]*$/{s/.*//;h;d;};h;n;p;x;G;brep' myfile.lisphist > myfile.lisp
魔法!你有一个有效的lisp程序。(显然,您应该清理它;其目的是保存您的历史记录,而不是将其用作程序。但它只包含您输入的lisp命令,而不是输出/结果,因此可以直接用作程序文件。)

然后您可以运行
clisp-repl myfile.lisp
和瞧!你又回到了上节课的状态。当然,要以交互方式加载它,您应该取出最后一行

对于
sed
命令,它所做的是查找
clisp
提示符
[number]>
,然后打印该行(删除提示符),然后尝试平衡括号。如果成功,则扫描下一个提示;如果括号不平衡,它会打印行,直到它们平衡为止

实际上,可以稍微简化为:

sed -n -e '/^\[[0-9]\+\]> /{s///;p;:rep' -e 's/([^()]*)//;trep' -e '/^[^(]*$/d;h;n;p;x;G;brep' -e '}' myfile.lisphist > myfile.lisp

(注意:我不使用
emacs
;我只使用直接在命令行调用的
clisp
。我不知道SLIME中是否有滚动历史。

非常感谢您的详细解释!没问题!我喜欢尽可能鼓励Lisper.:-)我想知道您是否可以通过管道将一个进程作为其stdin发送到clisp,该进程将在读取stdin时记录stdin,并只是回显它。然后,为了在理论上重新加载它,只需让初始管道程序在开始读取真正的标准数据之前打印出日志。。。有点像黑客,不确定会不会奏效,但理论上。。。