2011-12-25 18 views
1

我想建立一個散列表(除其他動作),而read ing。我不希望哈希表具有全局範圍(但),所以我用宏和gensym來做這件事。在宏x內部,我正在定義一個類似於setf的宏s,但在散列表中定義了一個條目而不是在某處定義了一個符號。它爆炸了。我想我理解錯誤信息,但是如何使其工作?建立一個散列表gensym和宏小號

代碼:

#!/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) 

輸出:

*** - 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

這看起來很複雜,我敢打賭,你不需要任何東西那。一個宏,一個宏,一個讀取數據的EVALuation,一個字母的宏名,... – 2011-12-25 13:19:29

+0

通常我會同意:它太複雜了。但它是更多參與的一部分。我把它精簡到一個小問題,這個問題更容易提出。 – 2011-12-25 13:43:38

+0

你爲什麼要編碼一個哈希表作爲Lisp源代碼? – 2011-12-25 13:58:09

回答

3

macrolet你也定義一個宏,所以通常的規則適用,即你必須反引號的表達式,這是在運行時進行評估。像這樣:

(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'中兩個逗號之間的單引號感到困惑。我拿出那個單引號,它炸了。它在那裏做什麼? – 2011-12-25 13:42:55

+0

糟糕。弄清楚了。咄。 – 2011-12-25 14:20:53

4

只是猜測Bill可能真的想要什麼。

假設他想要將某些鍵映射爲某些值作爲文件中的配置。

這是程序方式。

  • 打開一個流的數據
  • 讀它作爲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根本不會被設置。哈希中的一些值將是lambda表達式,它可能會根據一天中的時間和其他環境因素設置fred。 – 2011-12-25 14:31:41

+1

@Bill Evans在Mariposa:看到我上面的編輯... – 2011-12-25 15:27:06

+0

Rainer,我還是這個新手。我在這裏看到的代碼示例,就像任何接觸新外語的代碼一樣,可以幫助我更加流利地使用該語言。非常感謝你!事實證明,哈希表不會同時被定義。我可能會在今天15:23,弗雷德明天在08:00,威爾瑪在下週的某個時候,貝蒂也許永遠不會。 – 2011-12-25 17:37:31