25

私は clojure に手を出していますが、この一般的な python イディオムに相当する clojure (および/または Lisp) を特定しようとして少し苦労しています。

慣用句では、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) に同等のイディオムはありますか? 私は本格的な単体テストライブラリを求めていません(まあ、私はそうですが、この質問ではそうではありません)、特定の状況でのみ実行されるコードをモジュールに含めたいだけです。私が取り組んできたコードを実行する簡単な方法ですが、通常のモジュール/名前空間のようにファイルをインポートできます。

4

8 に答える 8

28

コマンドラインからClojureスクリプトを何度も実行するのは慣用的ではありません。REPLはより優れたコマンドラインです。ClojureはLispであるため、Clojureを起動して同じインスタンスを永久に実行したままにし、再起動するのではなく、Clojureと対話するのが一般的です。実行中のインスタンスの関数を一度に1つずつ変更し、実行して、必要に応じて突くことができます。面倒で時間がかかる従来の編集/コンパイル/デバッグサイクルを回避することは、Lispsの優れた機能です。

単体テストの実行などを行う関数を簡単に記述し、実行したいときはいつでもREPLからそれらの関数を呼び出し、それ以外の場合は無視することができます。Clojureでは、を使用しclojure.contrib.test-is、テスト関数を名前空間に追加してから、を使用clojure.contrib.test-is/run-testsしてそれらすべてを実行するのが一般的です。

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

コマンドラインからClojureスクリプトを本当に実行したい場合は、さまざまな方法で実行できます。いくつかの議論については、Clojureメーリングリストを参照してください。

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=>

src/clj/clojure/main.cljこれがどのように機能するかを確認したい場合は、Clojureソースを参照してください。

もう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したがって、コマンドラインから:

$ 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.orgにはClojureコードのコンパイルに関する多くの情報があります。

于 2009-06-10T08:18:39.363 に答える
1

私はClojureを初めて使用しますが、Clojureグループに関するこのディスカッションは、解決策や回避策になる可能性があります。具体的には、4月17日の午後10時40分にStuartSierraが投稿したものです。

于 2009-06-10T02:28:39.617 に答える
1

Common Lisp では、 featuresで条件付き読み取りを使用できます。

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

cl:*features*にバインドされた機能のリストにシンボル :testing が含まれる場合、上記は読み込まれるだけで、ロード中に実行されます。

例えば

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

:testing を機能のリストに一時的に追加します。

独自の機能を定義して、Common Lisp システムがどの式を読み取り、どの式をスキップするかを制御できます。

さらに、次のこともできます。

#-testing (print '|we are in production mode|)
于 2009-06-10T04:33:24.633 に答える
1

http://rosettacode.org/wiki/Scripted_Main#Clojureにもさまざまな可能性のリストがあります。(新しいものを見つけたら、追加してください。;-))

于 2011-05-19T16:17:58.323 に答える
0

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

于 2009-06-10T04:17:37.360 に答える
0

Bootはスクリプトをサポートするビルド ツール (leiningen の代替)です。#!/usr/bin/env bootしたがって、メソッドを持つことができる で始まるブートスクリプトを持つことができます-main

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

于 2016-02-10T15:19:51.427 に答える
0

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

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

失敗したテストを見つけたら、それを修正し、変更されたコードを再コンパイルして実験を続け、アプリケーション全体を再起動せずにテストを実行します。これにより、実行中のアプリケーションが多くの状態を蓄積している可能性があり (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 にバグを見つけたとします。この関数のコードに移動し、バグを修正してこの関数を再コンパイルします。次に、固定された関数を呼び出して、それが機能することを確認します。インメモリ データベースは消えていません。再コンパイル前と同じ状態です。

于 2009-06-10T06:10:12.303 に答える
-3

「エントリポイント」を持つことについて話している場合は、確かにそれを行うことができます:

(ns foo)

(defn foo [n]
  (inc n))

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

(main)

このコードが (load-file "foo.clj") または (use 'foo) または (require 'foo) されるたびに、(main) が呼び出されますが、通常は行われません。

より一般的なのは、コードのファイルが REPL でロードされ、メイン関数がユーザーによって呼び出されることです。

于 2009-06-18T04:11:40.377 に答える