编写从命令行执行但不在解释器中执行的Common Lisp代码 [英] Writing Common Lisp code that executes from the command line, but not inside the interpreter

查看:307
本文介绍了编写从命令行执行但不在解释器中执行的Common Lisp代码的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当编写Common Lisp代码时,我使用SLIME。特别是,我使用C-C C-k编译包含函数定义的缓冲区,然后切换到REPL来运行这些函数。将可执行代码运行在缓冲区中的那些函数似乎不能很好地工作。如果代码有错误,它可以使一个混乱。



这将是方便的包括没有编译在缓冲区的代码,但得到从命令行运行,例如当执行

  sbcl --script foo.lisp 

如果是这样,我不需要在每次我想从命令行运行代码时继续添加和删除代码。是否存在这样的条件?



这类似于Python条件。

  if __name __ =='__ main__':



此博客标题为

  ;;如果从命令行运行,以下if-test将成功

(if(>(length sb-ext:* posix-argv *)1)

;从命令行执行的代码只在这里


代码确实不是由SLIME中的编译器运行,但它不会由 sbcl --script 运行。



更新:感谢Vsevolod Dyomkin的有用的回答和后续。
关于该答案的一些注释跟着,从评论编译到那个答案。


  1. 首先,我问了一个问题,用于从命令行运行代码,但不是从解释器。
    提供的解决方案更多;它还提供了一种从解释器而不是命令行运行代码的方法。


  2. 第一步是定义一个阅读器宏函数的宏字符#!
    如链接遇到宏字符时,Lisp读者调用其读取器宏函数中所述。
    阅读器函数是通过调用 set-dispatch-宏字符
    所以,当看到#!字符时, set-dispatch-宏字符 lambda函数定义在要调用的主体中。
    然后,此函数将关键字:noscript 添加到 * features * 变量。
    另请参见有关什么阅读器宏在SO问题中有用的讨论


  3. 请注意,关键字 :noscript 被添加到 * features * ,正是当#!字符存在时。此外,当代码在解释器内部运行时,存在#!
    字符。当使用 slime ,但显然不存在(删除)从程序的

    文本 sbcl --script 。因此,当代码在interpeter中运行时,:noscript 被添加到 * features * a

    脚本。


  4. 我们现在使用内置读取器宏# - /#+ ,正如Vsevolod所说,它的行为类似于C的#IFDEF /#IFNDEF 。他们在 * features * 中检查符号

    。在这种情况下,# - :noscript 检查是否缺少:noscript +:noscript 检查是否存在:noscript

    如果满足这些条件,则运行相应的代码。要包装一段代码,可以使用
    progn 这样:# - :noscript(progn< your code here>)


  5. 最后,在运行使用此功能的代码之前,需要调用 set-dispatch-macro-character 。在 sbcl 的情况下,可以将
    放在初始化文件〜/ .sbclrc 中。注意,这种方法不依赖于Common Lisp实现是SBCL。


  6. 如sbcl-devel列表中提到的,一个更简单的替代方法是使用事实,当一个类型 *特征*

    a中的关键字:SWANK 使用SLIME。 SWANK是SLIME的服务器端。 SLIME应该更准确地称为SLIME / SWANK,因为这两个是客户端/服务器架构的客户端/服务器组件
    。我发现这篇博客文章名为了解
    SLIME
    ,这是有帮助的。 p>

    所以,可以使用# - :swank #+:swank 就像# - :noscript #+:noscript ,除了不需要写任何代码。
    当然,如果使用命令行解释器 sbcl ,这将不起作用,例如
    从那以后:SWANK 不会出现在 * features * 中。



解决方案

您可以使用以下技巧:


  1. 为shebang定义一个分派函数:

     (set-dispatch-macro- character#\##\!
    (lambda(stream cn)
    (declare(ignore cn))
    (read-line stream)
    `(eval-when(:compile-toplevel:load-toplevel:execute)
    (pushnew:noscript * features *)))


  2. <使用# - :noscript

     #!/ usr / local / bin / sbcl --script 

    (defun test(a)(print a))

    (test 1)
    # - :noscript b $ b#+:noscript(test 3)

    执行 ./ test .lisp 将打印1和2,而 Cc Ck 将输出1和3.


EDITS



这个技巧应该可以工作,因为shebang行被 sbcl --script ,但是当文件通过SLIME或其他机制加载时,不会删除。



这种方法的缺点是条件在:noscript 中不存在:script 。要修改它,应该在 sbcl --script 中处理本身。


When writing Common Lisp code, I use SLIME. In particular, I compile the buffer containing definitions of functions by using C-C C-k, and then switch to the REPL to run those functions. Putting executable code to run those functions in the buffer does not appear to work so well. If the code has bugs it can make a mess.

It would be handy to have a way to include code that doesn't get compiled in the buffer, but do get run from the command line, e.g. when doing

sbcl --script foo.lisp

If that were the case, I would not have to keep adding and removing code every time I wanted to run code from the command line. Does there exist such a condition?

This is analogous to the Python condition.

if __name__=='__main__':

which is false if a Python file is imported as a module, but true if it is run as a script.

This blog post entitled "Using SBCL for Common Lisp shell scripting", found by random Googling, has

;; If run from command line, the following if-test will succeed

(if (> (length sb-ext:*posix-argv*) 1)

    ;; Code that is executed from the command line only goes here

)

The code included indeed does not get run by the compiler inside SLIME, but it doesn't get run by sbcl --script either.

UPDATE: Thanks to Vsevolod Dyomkin for his helpful answer and the followups. Some notes about that answer follow, compiled from the comments to that answer. @Vsevolod, if you add these to your answer, I'll delete them.

  1. First, I asked for a way to run code from the command line, but not from the interpreter. The supplied solution does more; it also gives a way to run code from the interpreter but not the command line.

  2. The first step is to define a reader macro function for the macro character #!. As stated in the link "Upon encountering a macro character, the Lisp reader calls its reader macro function". The reader function is defined by the call to set-dispatch-macro-character. So, when the #! character is seen, the set-dispatch-macro-character causes the lambda function defined in the body to be called. This function then adds the keyword :noscript to the *features* variable. See also a discussion of what reader macros are good for in the SO question Read macros: what do you use them for?.

  3. Observe that the keyword :noscript is added to *features* precisely when the #! character is present. Furthermore, the #! character is present when the code is run inside the interpreter e.g. when using slime, but apparently is absent (removed) from program's
    text by sbcl --script is run. Therefore, :noscript is added to *features* when the code is run in the interpeter, but not when run as a
    script.

  4. We now use the builtin reader macros #-/#+, which as Vsevolod said, behave similarly to the to C's #IFDEF/#IFNDEF. They check for a symbol
    in *features*. In this case, #-:noscript checks for the absence of :noscript, and #+:noscript checks for the presence of :noscript.
    If those conditions are satisfied, it runs the corresponding code. To wrap a block of code, one can use progn like this: #-:noscript (progn <your code here>).

  5. Finally, one needs to call set-dispatch-macro-character before running code that uses this functionality. In the case of sbcl, one can put it in the initialization file ~/.sbclrc. Observe that this approach does not depend on the Common Lisp implementation being SBCL.

  6. A simpler alternative, as mentioned in the sbcl-devel list, is to use the fact that the keyword :SWANK appears when one types *features* in
    a REPL inside emacs using SLIME. SWANK is the server side of SLIME. SLIME should probably more accurately called SLIME/SWANK, as these two are the client/server components of a client-server architecture. I found this blog post called Understanding SLIME, which was helpful.

    So, one can use #-:swank and #+:swank just like #-:noscript and #+:noscript, except that one doesn't need to write any code. Of course, this then won't work if one is using the command line interpreter sbcl, for instance, since then :SWANK will not appear in *features*.

解决方案

You can use the following trick:

  1. Define a dispatch function for shebang:

    (set-dispatch-macro-character #\# #\!
        (lambda (stream c n)
          (declare (ignore c n))
          (read-line stream)
          `(eval-when (:compile-toplevel :load-toplevel :execute)
             (pushnew :noscript *features*))))
    

  2. In your script file use #-:noscript:

    #!/usr/local/bin/sbcl --script
    
    (defun test (a) (print a))
    
    (test 1)
    #-:noscript (test 2)
    #+:noscript (test 3)
    

    Executing ./test.lisp will print 1 and 2, while C-c C-k will output 1 and 3.

EDITS

This trick should work, because the shebang line is removed altogether by sbcl --script, but not removed, when the file is loaded through SLIME or other mechanisms.

The drawback of this approach is that we condition on absence of :noscript in features, and not presence of :script. To amend it, pushing of the appropriate feature should be done in sbcl --script processing itself.

这篇关于编写从命令行执行但不在解释器中执行的Common Lisp代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆