2011-12-25 6 views
1

私はread ingの間に(他のアクションの中で)ハッシュテーブルを構築しようとしています。私はハッシュテーブルにグローバルスコープ(まだ)を持たせたくないので、マクロとgensymでこれをやっています。 xのマクロの中でsetfに似ているが、どこかにシンボルを定義するのではなく、ハッシュテーブルのエントリを定義するマクロsを定義しています。それは爆破する。私はエラーメッセージを理解していると思いますが、どのように動作させるのですか?gensymとmacroletを使ってハッシュテーブルを構築する

コード:

#!/usr/bin/clisp -repl 

(defmacro x (&rest statements) 
    (let ((config-variables (gensym))) 
    `(macrolet ((s (place value) 
        (setf (gethash 'place ,config-variables) value))) 
     (let ((,config-variables (make-hash-table :test #'eq))) 
     (progn ,@statements) 
     ,config-variables)))) 

(defun load-config() 
    (let ((config-file-tree (read *standard-input*))) 
    (eval config-file-tree))) 

(defun load-test-config() 
    (with-input-from-string (*standard-input* "(x (s fred 3) (s barney 5))") 
    (load-config))) 

(load-test-config) 

出力:であること、あなたにもマクロを定義しているので、通常の規則が適用され、あなたが式をバッククォートする必要がある。すなわちmacrolet

*** - LET*: variable #:G12655 has no value 
The following restarts are available: 
USE-VALUE  :R1  Input a value to be used instead of #:G12655. 
STORE-VALUE :R2  Input a new value for #:G12655. 
SKIP   :R3  skip (LOAD-TEST-CONFIG) 
STOP   :R4  stop loading file /u/asterisk/semicolon/build.l/stackoverflow-semi 
+0

これは私があなたが何を必要としないことを賭けるだろうと非常に複雑に見えますそれ。マクロ、マクロレット、読み込みデータの評価、1文字のマクロ名、... –

+0

通常、私は同意します:それはあまりにも複雑です。しかし、それはもっと関わっているものの一部です。私は一口サイズの状況にそれを蒸留したので、質問するのが簡単になりました。 –

+0

なぜLispソースコードとしてハッシュテーブルをエンコードしたいのですか? –

答えて

3

実行時に評価されます。このように:

(defmacro x (&rest statements) 
    (let ((config-variables (gensym))) 
    `(macrolet ((s (place value) 
       `(setf (gethash ',place ,',config-variables) ,value))) 
     (let ((,config-variables (make-hash-table :test #'eq))) 
     (progn ,@statements) 
     ,config-variables)))) 
+0

完璧に動作します、ありがとう。 '、 '、config-variables'の2つのコンマの間にある一重引用符に困惑しています。私はその一重引用符を取り出し、それは爆破した。そこには何がありますか? –

+0

理解した。ダー。 –

4

実際にビルが望むものを推測します。

ファイルの設定として、いくつかのキーからいくつかの値へのマッピングが必要な場合を考えてみましょう。

手順は次のとおりです。

  • データストリームを開くには、S式
  • として読み出されたデータを歩くと

例コードハッシュテーブルを埋める:

(defun read-mapping (&optional (stream *standard-input*)) 
    (destructuring-bind (type &rest mappings) (read stream) 
    (assert (eq type 'mapping)) 
    (let ((table (make-hash-table))) 
     (loop for (key value) in mappings 
      do (setf (gethash key table) value)) 
     table))) 

(defun load-config() 
    (read-mapping)) 

(defun load-test-config() 
    (with-input-from-string (*standard-input* "(mapping (fred 3) (barney 5))") 
    (load-config))) 

(load-test-config) 

を使用:

CL-USER 57 > (load-test-config) 
#<EQL Hash Table{2} 402000151B> 

CL-USER 58 > (describe *) 

#<EQL Hash Table{2} 402000151B> is a HASH-TABLE 
BARNEY  5 
FRED  3 

利点:

  • データは、ソースコードと生成されたソースコード
  • でエンコードされませんノーマクロなし評価(!セキュリティ)EVALを経由しては、マクロ経由
  • 何のオブジェクトコードの膨張を必要としませんより大きいコードに拡大しています
  • 機能抽象度
  • もっと理解してデバッグするのがはるかに分かりやすい

また、{の読み取りマクロを書くと、{(fred 3) (barney 5)}がハッシュテーブルとして直接読み込まれるようになります。マクロにそれを回す

(defun make-table (mappings &aux (table (make-hash-table))) 
    (loop for (key value) in mappings 
     do (setf (gethash key table) (eval value))) 
    table) 

CL-USER 66> (describe (make-table '((fred (- 10 7)) (barney (- 10 5))))) 

#<EQL Hash Table{2} 4020000A4B> is a HASH-TABLE 
BARNEY  5 
FRED  3 


あなたが計算した値を持つようにしたい場合は

(defmacro defmapping (&body mappings) 
    `(make-table ',mappings)) 

(defmapping 
    (fred 3) 
    (barney 5)) 
+0

最も有益な、ありがとう。私は設定ファイルから値を簡単にロードしていないことが判明しました。この設定ファイルには実際のライブLISPコードがあります。 fredは時には3に設定され、時には4に設定され、他の時には決して設定されないことがあります。式が最初に評価されるとき、fredはまったく設定されません。ハッシュの値の中には、時刻やその他の環境上の考慮事項に基づいてfredを設定するラムダ式があります。 –

+1

@Bill Evans at Mariposa:上の私の編集を参照してください... –

+0

Rainer、私はまだこれで新しいです。私がここに見ているコードのサンプルは、新しい外国語への暴露のように、私が言語にもっと堪能できるように助けます。とてもありがとう!ハッシュテーブルはすべて同時に定義されるわけではないことが判明しました。私は今日15時23分にフレッドを、明日は08時に、バーニーは来週に、そしてベティは決して決してそうしないかもしれません。 –

関連する問題