In the program I am writing (actually, porting from Python) to help me
learn Lisp, I find myself using a construct like this fairly
frequently:
(let ((rubrik (gethash 'rubrik all-fields))
(section (gethash 'section all-fields))
(number (gethash 'number all-fields)))
(format t "~A-~A-~A" rubrik section number))
This, to my Lisp-virgin eyes, looks like a good opportunity for a
macro. I would like to be able to write something like this
(destructure-hashtable (rubrik section number) table
(format t "~A-~A-~A" rubrik section number))
and have it expand out to the let statement above (or something
comparable). My macro-fu skills aren't good enough to produce this at
all. I honestly can't quite wrap my mind around how it should be done.
Is this feasible? Can anyone out there give me any pointers?
Thanks,
Will.
Somethind like this?
(defmacro with-entries (entries table &body body)
(let ((obj (gensyms)))
`(let ((,obj ,table))
(symbol-macrolet ,(loop for entry in entries collect
`(,entry (gethash ',entry ,obj)))
,@body))))
(with-entries (a b c) (make-hash-table)
(setf a 1) (setf b 2) (print a) (print b))
Look also at CLOS objects and WITH-SLOTS.
Nicolas.
"Will McCutchen" <·········@gmail.com> schrieb im Newsbeitrag
····························@g49g2000cwa.googlegroups.com...
> In the program I am writing (actually, porting from Python) to help me
> learn Lisp, I find myself using a construct like this fairly
> frequently:
>
> (let ((rubrik (gethash 'rubrik all-fields))
> (section (gethash 'section all-fields))
> (number (gethash 'number all-fields)))
> (format t "~A-~A-~A" rubrik section number))
>
> This, to my Lisp-virgin eyes, looks like a good opportunity for a
> macro. I would like to be able to write something like this
>
> (destructure-hashtable (rubrik section number) table
> (format t "~A-~A-~A" rubrik section number))
>
> and have it expand out to the let statement above (or something
> comparable). My macro-fu skills aren't good enough to produce this at
> all. I honestly can't quite wrap my mind around how it should be done.
>
> Is this feasible? Can anyone out there give me any pointers?
>
> Thanks,
>
>
> Will.
>
I wrote:
(defmacro dh ((&rest fields) table &body body)
`(let (,@(mapcar (lambda (field) (list field (list 'gethash (list 'quote
field) table))) fields))
,@body))
where
(pprint (macroexpand '(dh (a b c) tb (blubb a))))
results to
(LET ((A (GETHASH 'A TB)) (B (GETHASH 'B TB)) (C (GETHASH 'C TB))) (BLUBB
A))
in LispWorks.
Andreas
"Sashank Varma" <············@yahoo.com> schrieb im Newsbeitrag
·····························@z14g2000cwz.googlegroups.com...
> Andreas Thiele wrote:
>
> > (defmacro dh ((&rest fields) table &body body)
> > `(let (,@(mapcar (lambda (field) (list field (list 'gethash (list
'quote
> > field) table))) fields))
> > ,@body))
>
> Nice. You can clean it up a bit more if you nest backquotes.
>
> (defmacro dh2 ((&rest fields) table &body body)
> `(let (,@(mapcar (lambda (field)
> `(,field (gethash ',field ,table)))
> fields))
> ,@body))
>
> Sashank
>
I wanted to leave something as exercise to the reader :)
Andreas
Andreas Thiele wrote:
> > Nice. You can clean it up a bit more if you nest backquotes.
> >
> > (defmacro dh2 ((&rest fields) table &body body)
> > `(let (,@(mapcar (lambda (field)
> > `(,field (gethash ',field ,table)))
> > fields))
> > ,@body))
>
> I wanted to leave something as exercise to the reader :)
D'oh!
"Will McCutchen" <·········@gmail.com> writes:
> In the program I am writing (actually, porting from Python) to help me
> learn Lisp, I find myself using a construct like this fairly
> frequently:
>
> (let ((rubrik (gethash 'rubrik all-fields))
> (section (gethash 'section all-fields))
> (number (gethash 'number all-fields)))
> (format t "~A-~A-~A" rubrik section number))
>
> This, to my Lisp-virgin eyes, looks like a good opportunity for a
> macro. I would like to be able to write something like this
>
> (destructure-hashtable (rubrik section number) table
> (format t "~A-~A-~A" rubrik section number))
>
> and have it expand out to the let statement above (or something
> comparable). My macro-fu skills aren't good enough to produce this at
> all. I honestly can't quite wrap my mind around how it should be done.
What about your function-fu?
> Is this feasible? Can anyone out there give me any pointers?
I find the name destructure-hashtable ill-indicated. There's no
destructuring at hand, you just fetch a set of values.
What about a function that would be called as:
(gen-multiple-gethash '(rubrik section number) 'hashtable '...)
and would return a s-expression like:
(let* ((T1 hashtable)
(rubrik (gethash 'rubrik T1))
(section (gethash 'section T1))
(number (gethash 'number T1)))
...)
We need to bind the hashtable to avoid multiple evaluation of an
expression, for example with: (gen-multiple-gethash '(a) '(aref hashes
(incf i)) '(print a))
Is your function-fu good enough to generate such a list?
;; rot13 to protect the innocents:
(qrsha tra-zhygvcyr-trgunfu (xrlf gnoyr &erfg obql)
(yrg ((ignoyr (traflz)))
`(yrg* ((,ignoyr ,gnoyr)
,@(zncpne (ynzoqn (xrl) `(,xrl (trgunfu ',xrl ,ignoyr))) xrlf))
,@obql)))
[165]> (gen-multiple-gethash '(rubrick section number)
'(aref hashes (incf i))
'(print rubrick) '(print section) '(print number))
(LET*
((#:G3806 (AREF HASHES (INCF I))) (RUBRICK (GETHASH 'RUBRICK #:G3806))
(SECTION (GETHASH 'SECTION #:G3806)) (NUMBER (GETHASH 'NUMBER #:G3806)))
(PRINT RUBRICK) (PRINT SECTION) (PRINT NUMBER))
[166]>
So now that you've got some function-fu, just add a thin layer of macro-fu:
(defmacro with-multiple-gethash (keys table &body body)
(apply (function gen-multiple-gethash) keys table body))
[169]> (macroexpand '(with-multiple-gethash (rubrick section number) (aref hashes (incf i))
(print rubrick) (print section) (print number)))
(LET*
((#:G3808 (AREF HASHES (INCF I))) (RUBRICK (GETHASH 'RUBRICK #:G3808))
(SECTION (GETHASH 'SECTION #:G3808)) (NUMBER (GETHASH 'NUMBER #:G3808)))
(PRINT RUBRICK) (PRINT SECTION) (PRINT NUMBER)) ;
T
--
__Pascal Bourguignon__ http://www.informatimago.com/
Wanna go outside.
Oh, no! Help! I got outside!
Let me back inside!
From: Kalle Olavi Niemitalo
Subject: Re: Something like DESTRUCTURING-BIND for hashtables
Date:
Message-ID: <87zmtsshhg.fsf@Astalo.kon.iki.fi>
"Will McCutchen" <·········@gmail.com> writes:
> I would like to be able to write something like this
>
> (destructure-hashtable (rubrik section number) table
> (format t "~A-~A-~A" rubrik section number))
(defmacro destructure-hashtable ((&rest vars) table-form &body body)
(let ((table-var (gensym)))
;; These two LETs could be written as one LET*, but see below.
`(let ((,table-var ,table-form))
(let ,(loop for var in vars
collect `(,var (gethash ',var ,table-var)))
,@body))))
You can even replace the inner LET with a SYMBOL-MACROLET so that
the hash table can be modified via the fake variables.
However, this seems like an unusual use for hash tables.
I'd probably use structure-objects or standard-objects instead.
See also the WITH-SLOTS macro.
Also, destructuring normally supports recursion: an object that
was found by destructuring a data structure can itself be further
destructured with a nested destructuring pattern. Because this
macro does not support anything like that, it should be called
something else. I considered WITH-HASH-KEYS and WITH-HASH-VALUES
(see CLHS Section 6.1.2.1.6, for-as-hash) but couldn't decide
which one (if either) would be right.
From: Thomas A. Russ
Subject: Re: Something like DESTRUCTURING-BIND for hashtables
Date:
Message-ID: <ymik6kvtq8m.fsf@sevak.isi.edu>
Kalle Olavi Niemitalo <···@iki.fi> writes:
> (defmacro destructure-hashtable ((&rest vars) table-form &body body)
...
> However, this seems like an unusual use for hash tables.
> I'd probably use structure-objects or standard-objects instead.
> See also the WITH-SLOTS macro.
Perhaps a more reasonable name of this macro would be WITH-HASH-VALUES.
--
Thomas A. Russ, USC/Information Sciences Institute
Kalle Olavi Niemitalo wrote:
> "Will McCutchen" <·········@gmail.com> writes:
> > I would like to be able to write something like this
> >
> > (destructure-hashtable (rubrik section number) table
> > (format t "~A-~A-~A" rubrik section number))
>
> (defmacro destructure-hashtable ((&rest vars) table-form &body body)
> (let ((table-var (gensym)))
> ;; These two LETs could be written as one LET*, but see below.
> `(let ((,table-var ,table-form))
> (let ,(loop for var in vars
> collect `(,var (gethash ',var ,table-var)))
> ,@body))))
>
> You can even replace the inner LET with a SYMBOL-MACROLET so that
> the hash table can be modified via the fake variables.
>
> However, this seems like an unusual use for hash tables.
> I'd probably use structure-objects or standard-objects instead.
> See also the WITH-SLOTS macro.
I think something like this would be useful:
(with-hash-values ((k1 "key1") (k2 "key2") (k3 :key3))
table
(setf k2 "new hash-value for k2")
(setf k3 "new hash-value for k3")
...)
So you can have fairly arbitrary hash-keys. And a shortcut for the
original poster can be
(with-hash-values k1 ...)
instead of
(with-hash-values (k1 'k1) ...)
though I don't know whether it's really useful in the long run.
Essentially Nicolas's code:
(defmacro with-hash-values (entries table &body body)
"Like WITH-SLOTS, but different."
(let ((=table= (gensym "table-")))
`(let ((,=table= ,table))
(symbol-macrolet
,(loop for entry in entries collect
(cond ((and (listp entry)
(= (length entry) 2)
(symbolp (first entry)))
(destructuring-bind (varname hash-key)
entry
`(,varname (gethash ,hash-key ,=table=))))
((symbolp entry)
`(,entry (gethash ',entry ,=table=)))
(t
(error "In WITH-HASH-VALUES, each member of
ENTRIES should either be a variable-name or list of two objects with
the first being a variable name: ~S" entry))))
,@body))))
Tayssir