5

このように定義された関数仕様があり、それを関数オブジェクトに評価して、受け渡しできるようにしたいと考えています。

(def spec '(foo [n] (* 2 n)))

このようなマクロを作成できます

(defmacro evspec [name arg & body] `(defn ~name [~arg] ~@body))

次に、次の呼び出しで関数 foo が得られます。(foo 3) を 3 で呼び出すと、6 が返されます。

(evspec foo n (* 2 n))

ただし、上記で定義した仕様から関数本体を取得すると、返された関数 foo は本体フォーム (* 2 n) を評価せず、代わりに本体フォームを返します。

(let [foo (first spec) arg (first (second spec)) body (last spec)]
  (evspec foo arg body))

user=> (foo 3)
(* 2 n)

今作成された foo 関数が $eval$foo であることに気付きました

user=> foo
#<user$eval766$foo__767 user$eval766$foo__767@39263b07>

作業中の foo 関数は

user=> foo
#<user$foo user$foo@66cf7fda>

なぜ違いがあり、どうすればそれを機能させることができるのか、誰でも説明できますか? 私は eval に返信せずに方法を持ちたいですか? javascript のバックグラウンドから来ているので、どういうわけか私はいつも eval が悪だと思っています。

4

1 に答える 1

6

なしで一般的にそれを行うことは単に不可能evalです。マクロは、コンパイル時に文字通り引数式が渡される単純な関数です (実行時にそれらの値が何であるかを知ることが一般的に根本的に不可能な場合)。特に、戻り値が である質問テキストevspec内のフォーム内への呼び出しでは、マクロ エキスパンダーは文字通りシンボルとシンボルをその位置引数として、(単一のシンボルを含む seq ) をその「残りの」引数として認識します。 ; 戻り値はこれらの入力と一致しています。let(* 2 n)evspecfoon(body)body

ただし、evalこのような目的での使用はまったく問題ありません。かなりのランタイム コストがかかることを覚えておくことが重要です。そのため、多少控えめに使用することをお勧めしますeval

また、JavaScriptevalではテキストを操作するのに対し、Clojureevalは Clojure のデータ構造 (実際、マクロが操作するのと同じデータ構造) を操作することに注意してください。これにより、エラーが発生しにくくなります。

于 2013-07-10T00:48:22.080 に答える