I've been search the hyperspec for a solution to this problem, and
ended up writing this code:
(defun list-to-hash (rest)
(let ((hash (make-hash-table)))
(labels ((genera-hash (lista)
(when (not (null lista))
(setf (gethash (car lista) hash) (cadr lista))
(genera-hash (cddr lista)))))
(genera-hash rest))
hash))
The questions:
1.- Is there a function to do this?
2.- If there is, how can improve my searching methods for hyperspec? :)
3.- If there is not, any way to improve my code or making it more
elegant?
Bye.
> * javuchi <·······@tznvy.pbz> [2005-11-28 07:52:44 -0800]:
>
> I've been search the hyperspec for a solution to this problem, and
> ended up writing this code:
>
> (defun list-to-hash (rest)
> (let ((hash (make-hash-table)))
> (labels ((genera-hash (lista)
> (when (not (null lista))
> (setf (gethash (car lista) hash) (cadr lista))
> (genera-hash (cddr lista)))))
> (genera-hash rest))
> hash))
>
> The questions:
>
> 1.- Is there a function to do this?
I don't think so.
> 3.- If there is not, any way to improve my code or making it more
> elegant?
(defun plist-to-hash (plist &optional (ht (make-hash-table :test 'eq)))
(setf (gethash (pop plist) ht) (pop plist))
(if plist (plist-to-hash plist ht) ht))
note that both your version and my simplification are not quite correct:
suppose plist=(a 10 a 20)
then (getf plist 'a) ==> 10
but (gethash 'a (plist-to-hash plist)) ==> 20
the correct version would be
(defun plist-to-hash (plist &optional (ht (make-hash-table :test 'eq)))
(let ((key (pop plist)) (val (pop plist)))
(unless (nth-value 1 (gethash key ht))
(setf (gethash key ht) val)))
(if plist (plist-to-hash plist ht) ht))
--
Sam Steingold (http://www.podval.org/~sds) running w2k
http://www.honestreporting.com http://truepeace.org
http://www.savegushkatif.org http://ffii.org/ http://www.dhimmi.com/
Single tasking: Just Say No.
"javuchi" <·······@gmail.com> wrote in message
·····························@o13g2000cwo.googlegroups.com...
> I've been search the hyperspec for a solution to this problem, and
> ended up writing this code:
>
> (defun list-to-hash (rest)
> (let ((hash (make-hash-table)))
> (labels ((genera-hash (lista)
> (when (not (null lista))
> (setf (gethash (car lista) hash) (cadr lista))
> (genera-hash (cddr lista)))))
> (genera-hash rest))
> hash))
> The questions:
>
> 1.- Is there a function to do this?
> 2.- If there is, how can improve my searching methods for hyperspec? :)
> 3.- If there is not, any way to improve my code or making it more
> elegant?
For a simple list traversal I would recommed a loop rather than recursion.
(defun list-to-hash (list &key test size rehash-size rehash-threshold)
(let ((hash (make-hash-table test size rehash-size rehash-threshold)))
(dolist (pair list hash)
(setf (gethash (car pair) hash) (cadr pair)))))
I also advise that for any "wrapper" function where you add something to a
standard function that you be sure to allow all the parameters of the
original.
--
Coby Beck
(remove #\Space "coby 101 @ bigpond . com")
javuchi wrote:
> I've been search the hyperspec for a solution to this problem, and
> ended up writing this code:
>
> (defun list-to-hash (rest)
> (let ((hash (make-hash-table)))
> (labels ((genera-hash (lista)
> (when (not (null lista))
> (setf (gethash (car lista) hash) (cadr lista))
> (genera-hash (cddr lista)))))
> (genera-hash rest))
> hash))
(loop with hash-table = (make-hash-table)
for (key value) on list by #'cddr
do (setf (gethash key hash-table) value)
finally (return hash-table))
The implicit destructuring in the for clause makes this more readable
IMHO, apart from the fact that I tend to prefer iteration over recursion.
That's the only idiom that comes closest to a (ANSI-defined) standard
functionality for iterating through property lists, AFAIK.
Other people have implemented a doplist macro that you may want to
google for.
> The questions:
>
> 1.- Is there a function to do this?
> 2.- If there is, how can improve my searching methods for hyperspec? :)
> 3.- If there is not, any way to improve my code or making it more
> elegant?
The property lists are not so well categorized in the HyperSpec. I guess
that's a historical accident because originally, property lists were
associated with symbols, so you can find some discussion about them in
the section about symbols instead of the section about conses. (In other
words, there is a section called "Lists as Association Lists" but no
corresponding "Lists as Property Lists" which would have been nice.)
Pascal
--
My website: http://p-cos.net
Closer to MOP & ContextL:
http://common-lisp.net/project/closer/
"javuchi" <·······@gmail.com> writes:
> I've been search the hyperspec for a solution to this problem, and
> ended up writing this code:
>
> (defun list-to-hash (rest)
> (let ((hash (make-hash-table)))
> (labels ((genera-hash (lista)
> (when (not (null lista))
> (setf (gethash (car lista) hash) (cadr lista))
> (genera-hash (cddr lista)))))
> (genera-hash rest))
> hash))
> The questions:
>
> 1.- Is there a function to do this?
Not to my limited knowledge.
> 3.- If there is not, any way to improve my code or making it more
> elegant?
I'd write it like:
(defun list-to-hash (rest)
(let ((hash (make-hash-table)))
(dolist (element rest hash)
(setf (gethash (car element) hash) (cdr element)))))
(I'm assuming you actually wanted the key to be the car of each
element in the list, not the whole element as I think your solution
does.)
--
-asbjxrn
···@while.it.lasted.invalid (Asbj�rn Bj�rnstad) writes:
> "javuchi" <·······@gmail.com> writes:
>
> > I've been search the hyperspec for a solution to this problem, and
> > ended up writing this code:
> >
> > (defun list-to-hash (rest)
> > (let ((hash (make-hash-table)))
> > (labels ((genera-hash (lista)
> > (when (not (null lista))
> > (setf (gethash (car lista) hash) (cadr lista))
> > (genera-hash (cddr lista)))))
> > (genera-hash rest))
> > hash))
>
> > The questions:
> >
> > 1.- Is there a function to do this?
>
> Not to my limited knowledge.
>
> > 3.- If there is not, any way to improve my code or making it more
> > elegant?
>
> I'd write it like:
>
> (defun list-to-hash (rest)
> (let ((hash (make-hash-table)))
> (dolist (element rest hash)
> (setf (gethash (car element) hash) (cdr element)))))
>
> (I'm assuming you actually wanted the key to be the car of each
> element in the list, not the whole element as I think your solution
> does.)
Reading Sams reply I realise I assumed too much, and misread your code
as well. Oh well, please disregard.
--
-asbjxrn
"javuchi" <·······@gmail.com> writes:
> I've been search the hyperspec for a solution to this problem, and
> ended up writing this code:
>
> (defun list-to-hash (rest)
> (let ((hash (make-hash-table)))
> (labels ((genera-hash (lista)
> (when (not (null lista))
> (setf (gethash (car lista) hash) (cadr lista))
> (genera-hash (cddr lista)))))
> (genera-hash rest))
> hash))
>
> The questions:
>
> 1.- Is there a function to do this?
No.
> 2.- If there is, how can improve my searching methods for hyperspec? :)
Mu.
> 3.- If there is not, any way to improve my code or making it more
> elegant?
FSVO elegant. The question is that of the usage pattern.
You are filling the hash table from a plist. This is all right if the
list comes from the user (less parentheses to write). But more often
associations in lists generated automatically are in the form of alist
(just check the number of alist specific functions vs. plist specific
functions).
So, if you will create a hash table from a literal list, you'd rather
call it as:
(hash k1 v1 k2 v2) ; like (list e1 e2 e3) or (vector i1 i2 i3)
or:
(let ((alist (compute-some-alist-such-as '((k1 . v1) (k2 . v2)))))
(alist-to-hash alist))
rather than: (list-to-hash (list k1 v1 k2 v2))
So you'd write:
(defun hash (&rest args)
(loop :with table = (make-hash-table)
:for (key value) :on args :by (function cddr)
:do (setf (gethash key table) value)
:finally (return table)))
Note that both in the case of plist and alist, if a key is present
more than once, then only the first value is taken into account.
(defun alist-to-hash (alist)
(loop :with table = (make-hash-table)
:with absent = (gensym)
:for assoc :in alist
:when (eq absent (gethash (car assoc) table absent))
:do (setf (gethash (car assoc) table) (cdr assoc))
:finally (return table)))
[269]> (hash 'one 1 'two 2 'one '(- 2 1))
#S(HASH-TABLE :TEST EXT:FASTHASH-EQL (TWO . 2) (ONE . (- 2 1)))
[270]> (alist-to-hash '((one . 1) (two . 2) (one . (- 2 1))))
#S(HASH-TABLE :TEST EXT:FASTHASH-EQL (TWO . 2) (ONE . 1))
[271]>
--
__Pascal Bourguignon__ http://www.informatimago.com/
Our enemies are innovative and resourceful, and so are we. They never
stop thinking about new ways to harm our country and our people, and
neither do we. -- Georges W. Bush
Thanks to all your comments. I take notes from them, and construct even
a better version, in which I take two utilities instead of just one:
(defun map-properties (fun lista)
(loop for (key value) on lista by #'cddr
do (funcall fun key value)
finally (return lista)))
(defun list-to-hash (lista)
(let ((hash (make-hash-table)))
(map-properties #'(lambda (key value)
(setf (gethash key hash) value))
lista)
hash))
Have fun.
"javuchi" <·······@gmail.com> writes:
> Thanks to all your comments. I take notes from them, and construct even
> a better version, in which I take two utilities instead of just one:
>
> (defun map-properties (fun lista)
> (loop for (key value) on lista by #'cddr
> do (funcall fun key value)
> finally (return lista)))
It could be more useful to collect the result of the function calls:
(defun map-plist (fun plist)
(loop :for (key value) on plist by #'cddr
:collect (funcall fun key value))
(defun map-alist (fun alist)
(loop :for (key . value) in alist
:collect (funcall fun key value))
Then you can write:
(map-plist (lambda (k v) (cons k (1+ v))) '(one 1 two 2 three 3))
or:
(map-alist (lambda (k v) (cons k (1+ v)))
(mapcar (function cons) '(one two three) '(1 2 3)))
> (defun list-to-hash (lista)
> (let ((hash (make-hash-table)))
> (map-properties #'(lambda (key value)
> (setf (gethash key hash) value))
> lista)
> hash))
If you write lista we don't know if you mean an a-list or a p-list!
--
__Pascal Bourguignon__ http://www.informatimago.com/
-----BEGIN GEEK CODE BLOCK-----
Version: 3.12
GCS d? s++:++ a+ C+++ UL++++ P--- L+++ E+++ W++ N+++ o-- K- w---
O- M++ V PS PE++ Y++ PGP t+ 5+ X++ R !tv b+++ DI++++ D++
G e+++ h+ r-- z?
------END GEEK CODE BLOCK------
Both Pascals, Sam and others:
I have realized that I lack:
1.- A better understanding of basic Lisp... as I don't fully understand
the difference between alist and plist. Perhaps my method of starting
learning Lisp with OnLisp might not be correct... :)
2.- I need to learn another language... the Lisp LOOP one! :)
I take note of your notes, and I publish here some fun code I've done
this morning. Sure you can find 1001 errors:
(proclaim '(inline list-to-hash map-properties hash filter group
plist-from-hash list-from-hash-keys list-from-hash-values
list-from-properties list-from-hash))
(defun list-to-hash (lista)
(let ((hash (make-hash-table)))
(map-properties #'(lambda (key value)
(setf (gethash key hash) value))
lista)
hash))
(defun map-properties (fun lista)
(mapcan #'(lambda (propertie)
(funcall fun (car propertie) (cadr propertie)))
(group lista 2)))
(defun hash (&rest args)
(list-to-hash args))
;devuelve el valor de un hash dentro de otro hash:
(defmacro re-gethash (key1 key2 hash)
`(gethash ,key2 (gethash ,key1 ,hash)))
(defun filter (fun lista)
(let ((nueva nil))
(dolist (x lista)
(let ((val (funcall fun x)))
(if val (push val nueva))))
(nreverse nueva)))
; agrupa listas en n valores
(defun group (source n)
(labels ((rec (source n)
(if (consp (nthcdr n source)) ; ¿es la lista mayor que n?
(cons (subseq source 0 n) (rec (subseq source n) n))
(list source))))
(if (>= n 1) (rec source n) nil)))
(defun plist-from-hash (hash)
(let ((lista nil))
(maphash #'(lambda (key value)
(push (list key value) lista))
hash)
lista))
(defun list-from-hash-values (hash)
(mapcar #'cadr (plist-from-hash hash)))
(defun list-from-hash-keys (hash)
(mapcar #'car (plist-from-hash hash)))
(defun list-from-properties (properties)
(mapcan #'list
(mapcar #'car properties)
(mapcar #'cadr properties)))
(defun list-from-hash (hash)
(list-from-properties (plist-from-hash hash)))
"javuchi" <·······@gmail.com> writes:
>
> Both Pascals, Sam and others:
>
> I have realized that I lack:
>
> 1=2E- A better understanding of basic Lisp...
...
> (proclaim '(inline list-to-hash map-properties hash filter group
> plist-from-hash list-from-hash-keys list-from-hash-values
> list-from-properties list-from-hash))
Well, you shouldn't be worrying about INLINE declarations when you are
just beginning to learn the language. For the time being you should
just not worry about using declarations at all. In lisp they are
(mostly) used for efficiency reasons and are not required for
correctness. (special declarations being an exception, but you don't
have any of those.)
> (defun list-to-hash (lista)
> (let ((hash (make-hash-table)))
> (map-properties #'(lambda (key value)
> (setf (gethash key hash) value))
> lista)
> hash))
>
> (defun map-properties (fun lista)
> (mapcan #'(lambda (propertie)
> (funcall fun (car propertie) (cadr propertie)))
> (group lista 2)))
You might want to look at MAPC. It doesn't create any return values,
and the call to MAP-PROPERTIES in LIST-TO-HASH doesn't make any use of
the return value of the call.
But another relatively simple way to handle this particular issue would
be to use some other means of manipulating the lists:
;; The easy case. An Alist is of the form ((key . value) ...)
;; using MAPC
(defun alist-to-hash (alist)
(let ((hash (make-hash-table)))
(mapc #'(lambda (association-pair)
(setf (gethash (first association-pair) hash)
(rest association-pair)))
alist)
hash))
;; or iteratively using DOLIST
(defun alist-to-hash (alist)
(let ((hash (make-hash-table)))
(dolist (association-pair alist)
(setf (gethash (first association-pair) hash)
(rest association-pair)))
hash))
;; For plists. A Plist is of the form (key value ...)
;; A little tricky:
(defun plist-to-hash (plist)
(let ((hash (make-hash-table)))
(labels ((set-hash-from-plist (plist)
(when plist
(let ((key (pop plist))
(value (pop plist)))
(setf (gethash key hash) value))
(set-hash-from-plist plist))))
(set-hash-from-plist plist))
hash))
;; or using simpler operations:
(defun plist-to-hash (plist)
(let ((hash (make-hash-table)))
(labels ((set-hash-from-plist (plist)
;; Assumes a well-formed plist
(when plist
(setf (gethash (first plist) hash) (second plist))
(set-hash-from-plist (rest (rest plist))))))
(set-hash-from-plist plist))
hash))
> (defun hash (&rest args)
> (list-to-hash args))
It really isn't necessary to separate HASH and LIST-TO-HASH, unless you
really want to provide two different interfaces to the functions.
> ;devuelve el valor de un hash dentro de otro hash:
> (defmacro re-gethash (key1 key2 hash)
> `(gethash ,key2 (gethash ,key1 ,hash)))
>
> (defun filter (fun lista)
> (let ((nueva nil))
> (dolist (x lista)
> (let ((val (funcall fun x)))
> (if val (push val nueva))))
> (nreverse nueva)))
>
> ; agrupa listas en n valores
> (defun group (source n)
> (labels ((rec (source n)
> (if (consp (nthcdr n source)) ; =BFes la lista mayor que n?
You probably should just use the LENGTH function here. It would be a
bit clearer, although it does have to walk the entire length of the
list. It would also be useful if you named the inner function with a
more meaningful name than "rec".
> (cons (subseq source 0 n) (rec (subseq source n) n))
> (list source))))
> (if (>=3D n 1) (rec source n) nil)))
Actually, looking at this, it isn't clear why you need the inner
function with LABELS at all. You should be able to just put the entire
logic in line:
(defun group (source n)
(cond ((< n 1)
nil)
((consp (nthcdr n source))
(cons (subseq source 0 n) (group (subseq source n) n)))
(t
(list source))))
> (defun plist-from-hash (hash)
> (let ((lista nil))
> (maphash #'(lambda (key value)
> (push (list key value) lista))
> hash)
> lista))
Actually this isn't a plist. This produces a result which is
((key1 value1) (key2 value2) ...) instead of
(key1 value1 key2 value2 ...)
(defun plist-from-hash (hash)
(let ((lista nil))
(maphash #'(lambda (key value)
(push key lista)
(push value lista))
hash)
lista))
> (defun list-from-hash-values (hash)
> (mapcar #'cadr (plist-from-hash hash)))
>
> (defun list-from-hash-keys (hash)
> (mapcar #'car (plist-from-hash hash)))
This won't work quite so easily on real plists. It would work, with one
minor change on alists, though.
> (defun list-from-properties (properties)
> (mapcan #'list
> (mapcar #'car properties)
> (mapcar #'cadr properties)))
>
> (defun list-from-hash (hash)
> (list-from-properties (plist-from-hash hash)))
>
--
Thomas A. Russ, USC/Information Sciences Institute