0

私は Clojure のタグ付きリテラルを使用しようとしてきましたが、マクロのようにリーダーが引数を評価しないことに気付きました。これは理にかなっていますが、これを行うための適切な解決策は何ですか? 明示的な評価?

例: この関数が与えられた場合

(defn my-data
  ([[arg]]
     (prn (symbol? arg))
     :ok))

そしてこの定義data_readers.clj

{myns/my-data my.ns/my-data}

次の動作は異なります。

> (let [x 1] (my.ns/my-data [x]))
false
:ok

したがって、x渡された は に渡される前に評価されmy-dataます。一方で:

> (let [x 1] #myns/my-data [x])
true
:ok

xしたがって、 insideの値を使用したい場合my-datamy-data関数はそれについて何かを行う必要があります。つまり、それxがシンボルであることを確認し、そうであれば を使用します(eval x)。それは醜いようです。より良いアプローチはありますか?

4

1 に答える 1

3

概要

x主にローカルは実行時にのみ割り当てられた値を取得するのに対し、タグ付きリテラルは読み取り時に処理されるため、例でローカルの値を取得する方法はありません。(その間にもコンパイル時間があります。コンパイル時にローカルの値を取得することは不可能です。したがって、マクロもローカルの値を取得できません。) 1

より良いアプローチは、実行時に通常の関数を使用することです。これは、結局のところ、一部のパラメーターの実行時の値に基づいて値を作成する必要があるためです。タグ付きリテラルは実際にはリテラルであり、そのように使用する必要があります。

拡張ディスカッション

上記の問題を説明するには、次のようにします。

(binding [*data-readers* {'bar (fn [_] (java.util.Date.))}]
  (eval (read-string "(defn foo [] #bar x)")))

fooは常に同じ値を返します。これは、リーダーが値を返す機会が 1 回しかなく、その値がのバイトコード#bar xに焼き付けられるためです。foo

evalまた、直接渡す代わりに、呼び出しによって返されたデータ構造を保存read-stringし、将来の任意の時点でコンパイルできることにも注意してください。によって返される値はfoo同じままです。明らかに、そのようなリテラル値がローカルの将来の値に依存する方法はありません。実際、リーダーの操作中には、どのシンボルがローカルに名前を付けるようになるかさえ明確ではありません。これはコンパイラーが決定することであり、その決定の結果は、マクロが関与する場合には明らかではない可能性があります。

もちろん、読者は関数呼び出しやローカルの名前などのように見えるフォームを自由に返すことができます。一例を示すには:

(binding [*data-readers* {'bar (fn [sym] (list sym 1 2 3 4 5))}]
  (eval (read-string "#bar *")))
;= 120
;; substituting + for * in the string yields a value of 15

ここ#bar fは と等価になり(f 1 2 3 4 5)ます。言うまでもなく、これは表記法を乱用したものであり、実際には要求したことを実行しません。


1evalはローカルにアクセスできない (常にグローバル スコープで動作する)ことは指摘しておく価値がありますが、実行前に値が割り当てられていないローカルの問題はより根本的なものです。

于 2013-11-09T22:33:36.233 に答える