Clojure中相当于Python "if __name__ == '__main__'" 的是什么?

25 投票
8 回答
3862 浏览
提问于 2025-04-15 12:08

我最近在尝试学习Clojure,但在找Clojure(或者Lisp)中与Python常用写法相对应的东西时遇到了一些困难。

这个写法的特点是,在Python模块的底部,通常会有一段测试代码,然后还有一句话来运行这些代码,比如:

# mymodule.py
class MyClass(object):
    """Main logic / code for the library lives here"""
    pass

def _runTests():
    # Code which tests various aspects of MyClass...
    mc = MyClass() # etc...
    assert 2 + 2 == 4

if __name__ == '__main__': _runTests()

这样做对于简单的临时测试很有用。通常情况下,我们会通过写 from mymodule import MyClass 来使用这个模块,这样 _runTests() 就不会被调用。但如果在最后加上这段代码,我们也可以直接在命令行输入 python mymodule.py 来运行它。

在Clojure(或者常见的Lisp)中有没有类似的写法呢?我并不是想要一个完整的单元测试库(虽然我确实想要,但这不是我现在要讨论的),我只是想在模块中包含一些代码,这些代码只会在特定情况下运行,这样我就可以快速测试我正在做的代码,同时又能让我的文件像普通模块/命名空间一样被导入。

8 个回答

1

我刚接触Clojure,但我觉得在Clojure小组里有一个讨论可能会有解决办法或者替代方案,特别是Stuart Sierra在4月17日晚上10:40发的那条帖子。

1

在Common Lisp中,你可以使用条件读取功能,具体来说就是用特性

#+testing (run-test 'is-answer-equal-42)

上面的代码只有在加载时,如果绑定到cl:*features*的特性列表中包含符号:test,那么它才会被读取并执行。

举个例子:

(let ((*features* (cons :testing *features*)))
   (load "/foo/bar/my-answerlib.lisp"))

这段代码会暂时把:test添加到特性列表中。

你还可以定义自己的特性,来控制Common Lisp系统读取哪些表达式,跳过哪些表达式。

此外,你还可以这样做:

#-testing (print '|we are in production mode|)
28

从命令行反复运行Clojure脚本并不是一种常见的做法。使用REPL(交互式命令行)会更好。因为Clojure是一种Lisp语言,通常我们会启动Clojure并让它一直运行,而不是每次都重启。这样,我们可以在运行的实例中逐个修改函数,随时运行和测试它们。这样可以避免传统的编辑、编译、调试的繁琐和缓慢过程,这是Lisp语言的一大优点。

你可以很容易地写一些函数来执行单元测试,然后在REPL中随时调用这些函数,其他时候就可以不管它们。在Clojure中,常用的做法是使用clojure.contrib.test-is,把你的测试函数添加到命名空间中,然后用clojure.contrib.test-is/run-tests来一次性运行所有测试。

还有一个不从命令行运行Clojure的好理由是,JVM(Java虚拟机)的启动时间可能会很长。

如果你真的想从命令行运行Clojure脚本,有很多方法可以做到这一点。可以查看Clojure邮件列表上的一些讨论。

一种方法是检查命令行参数的存在。假设当前目录下有一个foo.clj文件:

(ns foo)

(defn hello [x] (println "Hello," x))

(if *command-line-args*
  (hello "command line")
  (hello "REPL"))

根据你启动Clojure的方式,行为会有所不同。

$ java -cp ~/path/to/clojure.jar:. clojure.main foo.clj --
Hello, command line
$ java -cp ~/path/to/clojure.jar:. clojure.main
Clojure 1.1.0-alpha-SNAPSHOT
user=> (use 'foo)
Hello, REPL
nil
user=>

如果你想了解这个是如何工作的,可以查看Clojure源代码中的src/clj/clojure/main.clj

另一种方法是将你的代码编译成.class文件,并从Java命令行调用它们。假设有一个源文件foo.clj

(ns foo
  (:gen-class))

(defn hello [x] (println "Hello," x))

(defn -main [] (hello "command line"))

你需要创建一个目录来存放编译后的.class文件,默认是./classes。这个文件夹需要你自己创建,Clojure不会自动生成。同时,确保你设置了$CLASSPATH,包括./classes和你的源代码所在的目录;假设foo.clj在当前目录下。那么在命令行中:

$ mkdir classes
$ java -cp ~/path/to/clojure.jar:./classes:. clojure.main
Clojure 1.1.0-alpha-SNAPSHOT
user=> (compile 'foo)
foo

classes目录下,你现在会有一堆.class文件。要从命令行调用你的代码(默认运行-main函数):

$ java -cp ~/path/to/clojure.jar:./classes foo
Hello, command line.

关于编译Clojure代码的信息可以在clojure.org上找到。

撰写回答