2009-06-10 12 views
24

私はclojureでおしゃべりしていて、この共通のpythonイディオムに相当するclojure(および/またはLisp)を決定しようとするのに少し問題があります。Pythonイディオムのclojureに相当するものは "if __name__ == '__main__'"ですか?

イディオムは、例えばコードを実行文で、その後の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() 

これは、単純な、広告に便利です-hocテスト。通常はfrom mymodule import MyClassと書いてこのモジュールを使用します。この場合、_runTests()は決して呼び出されませんが、最後にスニペットを使用してコマンドラインから直接python mymodule.pyと入力して実行することもできます。

Clojure(および/または共通のlisp)に相当するイディオムはありますか?私は本格的な単体テストライブラリの後ではない(まあ、私は、この質問ではない)、私はちょうどいくつかの状況下で実行されるモジュールにいくつかのコードを含めることを望むので、私が作業しているコードを実行する簡単な方法ですが、通常のモジュール/名前空間のようにファイルをインポートすることができます。

答えて

27

コマンドラインからClojureスクリプトを何度も実行するのは慣れていません。 REPLはより良いコマンドラインです。 ClojureはLispであり、Clojureを起動し、同じインスタンスを永続的に実行させ、再起動するのではなく相互作用するのが一般的です。実行中のインスタンスの関数を一度に1つずつ変更して実行し、必要に応じてそれらをポックすることができます。退屈で遅い伝統的な編集/コンパイル/デバッグサイクルを免れることは、Lispsの大きな特徴です。

単体テストの実行などの機能を簡単に記述できます。実行するときはいつでもREPLから呼び出すことができます。 Clojureではclojure.contrib.test-isを使用し、テスト関数をネームスペースに追加してから、clojure.contrib.test-is/run-testsを使用してすべて実行します。

Clojureをコマンドラインから実行しないもう一つの理由は、JVMの起動時間が非常に長くなる可能性があることです。

コマンドラインからClojureスクリプトを実際に実行したい場合は、できることがたくさんあります。詳細は、the Clojure mailing listを参照してください。

1つの方法は、コマンドライン引数の有無をテストすることです。現在のディレクトリにこの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を参照してください。

もう1つの方法は、コードを.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が現在のディレクトリにあると仮定します。だから、コマンドラインから:classesディレクトリで

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

あなたは今.classファイルの束を持っています。コマンドライン(デフォルトで-main機能を実行している)から、あなたのコードを呼び出すには:

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

clojure.orgにClojureのコードをコンパイルに関する多くの情報があります。あなたは「エントリーポイント」を持っていることについて話している場合は

1

私はClojureの新機能ですが、Clojureグループのthis discussionは4月17日の午後10時40分にStuart Sierraが投稿した解決策や回避策です。

0

clojure-contribのtest-isライブラリを参照してください。それは同じイディオムではありませんが、かなり類似したワークフローをサポートするはずです。

1

Common Lispでは、featuresで条件付きの読書を使うことができます。

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

は読み取り専用ので、cl:*features*にバインドされた機能のリストは、シンボルが含まれます場合は、ロード中に実行されます:テストを。

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

例えば

は一時的に追加されます:機能のリストにテストを。

独自のフィーチャを定義し、Common Lispシステムがどの式を読み込み、スキップするかを制御できます。

またあなたも行うことができます。

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

私は*機能*がこれには良いとは思いません。 *機能*は利用可能な機能を示していますが、一部の環境状態やコードの実行要求ではありません。 –

+0

なぜですか? *すべての種類のものに使用される* features *:実行中のハードウェア、いくつかのコアlibs、ソフトウェアのいくつかのモード、Lisp実装のバージョン、言語のバージョン、 modeまたは:development-modeなど。 –

0

のCommon LispとClojureの(だけでなく、他のLisp)REPLとのインタラクティブな環境を提供し、あなたは«if __name__ == '__main__'»のようなトリックを必要としません。 PythonのためのREPLのような環境があります:コマンドラインからのpython、ipython、Emacsのpythonモードなど

ライブラリを作成し、テストスイートを追加するだけです(Common Lispのテストフレームワークはたくさんあります私は5amフレームワークを好む、利用可能なフレームワークの調査がありますhere)。ライブラリをロードすると、ライブラリを使って何かを行うことができます:テストの実行、関数の呼び出し、実験など

失敗したテストを見つけたら、変更したものを再コンパイルしますアプリケーション全体を再起動することなくテストを実行することができます。これは、実行中のアプリケーションが多くの状態を蓄積している可能性があるため(GUIウィンドウを作成したり、データベースに接続したり、簡単に再現できない重大な瞬間に達したため)、再起動する必要はありませんすべての変更の後に。

ここではCommon Lisp(私のCL-sqliteのライブラリーからの)のための例です:

コード:

(def-suite sqlite-suite) 

(defun run-all-tests() 
    (run! 'sqlite-suite));' 

(in-suite sqlite-suite) 

(test test-connect 
    (with-open-database (db ":memory:"))) 

(test test-disconnect-with-statements 
    (finishes 
    (with-open-database (db ":memory:") 
     (prepare-statement db "create table users (id integer primary key, user_name text not null, age integer null)")))) 
... 

と対話セッション:

CL-USER> (sqlite-tests:run-all-tests) 
....... 
Did 7 checks. 
    Pass: 7 (100%) 
    Skip: 0 (0%) 
    Fail: 0 (0%) 

NIL 
CL-USER> (defvar *db* (sqlite:connect ":memory:")) 
*DB* 
CL-USER> (sqlite:execute-non-query *db* "create table t1 (field text not null)") 
; No value 
CL-USER> (sqlite:execute-non-query *db* "insert into t1 (field) values (?)" "hello") 
; No value 
CL-USER> (sqlite:execute-to-list *db* "select * from t1") 
(("hello")) 
CL-USER> 

は今、私が見つけたと仮定sqlite:execute-to-listのバグ。私はこの関数のコードに行き、バグを修正してこの関数を再コンパイルします。それから、私は固定機能を呼び出し、それが機能することを保証します。メモリ内のデータベースはなくなりません。再コンパイルする前の状態と同じです。

+3

__name __ == '__ main__'イディオムは、REPLとはまったく関係ありません。これは、モジュールとしてインポートされたものとスクリプトとして実行されたものを区別する方法です。一般的には、REPLで試してみることのできるヌードルや試用コードではなく、同じコードを繰り返し実行したいと思うコードです。テストコードの一例ですが、最も一般的なのは通常、モジュールとして再利用できるスクリプトを持つことです。 – Brian

+0

はい、一般的に、それらは異なるものです。しかし、この質問の文脈では、テストを実行(および再実行)するために__name__のチェックが使用され、REPLはlispsのこのようなユースケースに対しては慣例的です。 –

+0

ユーザが名前== main idiomを要求しました.replではなく、テストスイートではありません。 – mcandre

-3

あなたは確かにそれを行うことができます。

(ns foo) 

(defn foo [n] 
    (inc n)) 

(defn main [] 
    (println "working") 
    (println "Foo has ran:" (foo 1))) 

(main) 

今何が起こるだろうことはいつでも、このコードがあるということである(ロード・ファイル「foo.clj」)」 d(または 'foo'を使う)、(require 'foo')、(main)を呼び出すと、通常は実行されません。

もっと一般的なことは、コードファイルをREPLにロードしてから、main関数がユーザーによって呼び出されるということです。

+0

これは、foo.cljが直接実行され、別のスクリプトがそれをロードしたときではなく、(main)がトリガされるようにすることができますか? – mcandre

+0

どちらの場合も、すべての式を評価する(そしてコンパイルする)ので、私はそうは思わない。エントリーポイントの定義を可能にするAOTコンパイルが常にあります:http://clojure.org/compilation – Chris

1

にもさまざまな候補のリストがあります。 (新しいものを見つけたら - ;-))

0

は、ビルドツール(leiningenの代替品)で、そのsupports scriptsです。そのため、#!/usr/bin/env bootで始まる起動スクリプトを-mainメソッドに含めることができます。

また、コードのさまざまな機能を呼び出すコマンドラインからタスクを呼び出すこともできます。また、これらの関数の1つをエントリーポイントとしてUberjarを作成できるパッケージングタスクを持つことができます。

関連する問題