10

API ドキュメントで、*file*変数には「評価されるファイルのパスを文字列として」の値が必要であると読んでいます。ただし、この機能は場合によっては壊れているようです。

を使用してファイルを実行すると、lein exec期待どおりに動作します。

$ cat test.clj
(println *file*)
$ lein exec test.clj 
/path/to/test.clj

しかし、への呼び出しを含むテストを実行すると(println *file*)NO_SOURCE_PATHその行を含むファイルの代わりに が出力されます。

この動作が見られるのはなぜですか? また、評価中のファイルのパスとファイル名に確実にアクセスするにはどうすればよいですか?

4

1 に答える 1

14

*file*はコンパイルされるファイルのパスに設定されるため、プログラム全体がコンパイルされた後は、 *file*(eval を使用しないと仮定して) の値を調べることはもはや役に立ちません。

あなたのtest.clj例ではprintln、ファイルがまだコンパイルされている間に実行されます。への参照が*file*テストまたは関数に移動された場合、 の値が*file*役に立たなくなった後、実行時にのみ逆参照されます。

1 つのオプションは、*file*展開されたときの値を格納するマクロを作成して、結果を後で使用できるようにすることです。たとえば、ファイルには次のようなexample.cljものがあります。

(defmacro source-file []
  *file*)

(defn foo [x]
  (println "Foo was defined in" (source-file) "and called with" x))

次に、REPL またはどこからでも、次の(foo 42)ように出力されます。

Foo was defined in /home/chouser/example.clj and called with 42

どのファイルsource-fileで定義されているかは問題ではなく、展開された場所、つまりが定義されているファイルであることに注意してくださいfoo。これが機能するのは、fooがコンパイルされたときにsource-file実行され、 の戻り値source-fileが単なる文字列である のコンパイル済みバージョンに含まれるためですfoofooもちろん、文字列は実行するたびに利用できます。

この動作が驚くべきものである場合は*file*、実行時に がすべての関数内で有用な値を持つために何が起こる必要があるかを検討することが役立つ場合があります。その値は、関数の呼び出しと戻りごとに変更する必要があり、めったに使用されない機能の場合、かなりの実行時オーバーヘッドになります。

于 2012-10-02T15:09:20.267 に答える