Clojure - (文字列読み込み関数read-string

私はclojureファイルに次のものを持っています:

(ns helloworld
  (:gen-class
    :main -main))

(defn hello-world-fn []
  (println "Hello World"))

(defn -main [& args]
  (eval (read-string "(hello-world-fn)")))

と私はそれを実行しています

lein run helloworld

次のエラーが表示されます。

Exception in thread "main" java.lang.RuntimeException: Unable to resolve symbol:
 helloworld in this context, compiling:(helloworld.clj:12)

私は ns-resolve resolve で何かをする必要があると感じましたが、何の成功もありませんでした。私は主な機能で次のことを試しました:

(let [call-string  (read-string "(hello-world-fn)")
      func (resolve  (symbol (first call-string)))
      args (rest call-string)]
   (apply func args))

成功なし。

誰か(a)が私を正しい方向に向けることができますか? (b)Clojureの読者が何が起こっているのかを正確に説明してください。

3

2 答え

実際のネームスペースが -main の内部にあるかどうか確認してください。

(defn -main [& args]
  (prn *ns*)
  (eval (read-string "(hello-world-fn)")))

It outputs # before bombing out with the exception. This hints that execution of programs with lein run starts out in the user namespace, which obviously does not contain the mapping for your hello-world-fn symbol. You'll need to explicitly qualify it.

(defn -main [& args]
  (eval (read-string "(helloworld/hello-world-fn)")))
5
追加された
本当にデバッグのアプローチが好きで、直接それを解決してください。私は他の方が箱から出て、よりエレガントであると思った。コンパイルの段階を説明する@MatthiasBenkardを感謝します。
追加された 著者 hawkeye,
(hello-world-fn)を直接呼び出すと動作します。 eval と異なる動作をする理由は何ですか?
追加された 著者 viebel,
@YehonathanSharvitこの場合、コンパイル時に hello-world-fn というシンボルが解決されるため、ソースファイルの先頭付近の名前空間宣言が関係します。一方、 "(hello-world-fn)" は単なる文字列です。コンパイラは文字列を名前空間に限定しません。修飾するのは間違っているためです。したがって、実行時に実行される eval は、実行時に * ns * 変数を調べて名前参照を解決する必要があります。
追加された 著者 Matthias Benkard,

マクロを使用すると、非常に上品な方法で課題を解決できます。実際、 eval を模倣するマクロを書くことができます。

(defmacro my-eval [s] `~(read-string s))
(my-eval "(hello-world-fn)")); "Hello World"

my-eval を呼び出すコンテキストで s のシンボル解決が行われるので、 eval の方が効果的です。説明のための@Matthias Benkardに感謝します。

You can read about macros and their syntax in http://clojure.org/reader

3
追加された
なぜns-resolveが自動的に名前空間の問題を解決するのですか?
追加された 著者 hawkeye,
ありがとう - 例では、 * ns * が既にhelloworldを指している場合 - その機能を解決できません
追加された 著者 hawkeye,
はるかにエレガントでフレキシブル。本当に@MatthiasBenkardの説明も好きです。
追加された 著者 hawkeye,
@hawkeye resolve は実行時に機能し、 * ns * 変数が指している現在の名前空間このとき、ファイルのコンテキスト内のマクロ展開されたコードは、ファイル内の名前空間宣言に従ってシンボルを解決するファイルコンパイラによって処理されます。
追加された 著者 Matthias Benkard,
@hawkeye実行時とコンパイル時を厳密に区別する必要があります。コンパイル時には、何かが内部でコンパイルされている名前空間が知られていますが、コンパイル後、コードは完全に解決され、名前空間で修飾されているため、実行時の現在のネームスペース eval に渡されるコードは、その時点の * ns * 変数に基づいて解決されます。コンパイル時。別の名前空間でコンパイルされた関数を呼び出すと(奇妙な動作、IMHO)、 * ns * 変数は魔法のように変わることはありません。 * ns * は手動でバインドする必要があります。
追加された 著者 Matthias Benkard,