automaton
automaton

Reputation: 1121

Declare global variable using an "artificial" symbol

By "artificial", I mean one created from a string using intern or make-symbol.
I have a section of my code that declares up to 49 global variables:

(defparameter *CHAR-COUNT-1-1* (make-hash-table))
...
(defparameter *CHAR-COUNT-1-7* (make-hash-table))
...
(defparameter *CHAR-COUNT-7-7* (make-hash-table))

I thought, instead, I could create a function to do all that:

(loop for n from 1 to 7 do
  (loop for i from 1 to 7 do
    (defparameter (symbol-value (intern (concatenate 'string "*CHAR-COUNT-" (write-to-string n) "-" (write-to-string i) "*")))
                  (make-hash-table :test 'equalp))))

But get the error(sbcl):

unhandled SIMPLE-ERROR in thread #<SB-THREAD:THREAD "main thread" RUNNING
                                    {1002978EE3}>:
  Can't declare a non-symbol as SPECIAL: (SYMBOL-VALUE
                                          (INTERN
                                           (CONCATENATE 'STRING "*CHAR-COUNT-"
                                                        (WRITE-TO-STRING N) "-"
                                                        (WRITE-TO-STRING I)
                                                        "*")))

What is the correct way to do this?

Upvotes: 0

Views: 158

Answers (3)

Svante
Svante

Reputation: 51501

The correct way is to use a proper data structure instead of encoding dimensions in symbol names. Do you really want to calculate and encode symbol names any time you want to access the correct table?

(defparameter *char-counts* (make-array '(7 7)))

(dotimes (i 49) ; or (reduce #'* (array-dimensions *char-counts*))
  (setf (row-major-aref *char-counts* i) (make-hash-table)))

Now you can access the array of tables just with the indices (x and y in this example):

(gethash (aref *char-counts* x y) :foo)

Upvotes: 1

Joshua Taylor
Joshua Taylor

Reputation: 85833

Defparameter is a macro, not a function. That means that it defines a special syntax. The defparameter form needs to have a symbol as its second argument, but you're providing the list:

(symbol-value (intern (concatenate 'string "*CHAR-COUNT-" (write-to-string n) "-" (write-to-string i) "*")))

What you want is a form like

(progn 
  (defparameter *foo-1-1* (make-hash-table ...))
  ...
  (defparameter *foo-n-n* (make-hash-table ...)))

You seem familiar enough with loop and creating the symbols to create that list; just change

(loop … do (loop … do (defparameter …)))

to

`(progn 
  ,@(loop … nconcing
      (loop … collecting
        `(defparameter ,(intern …) …))))

and you can get the form you need. Then it's just a matter of putting it all into a macro

(defmacro … (…) 
  `(progn 
     ,@(loop … nconcing
         (loop … collecting
          `(defparameter ,(intern …) …)))))

and calling the macro.

Upvotes: 5

Vatine
Vatine

Reputation: 21258

One of "use a macro that returns a PROGN with DEFPARAMETER stanzas" or "use PROCLAIM, it is a function, not a macro".

Upvotes: 2

Related Questions