From: javuchi
Subject: listo-to-hash
Date: 
Message-ID: <1133193164.084666.140430@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?

Bye.

From: Sam Steingold
Subject: Re: listo-to-hash
Date: 
Message-ID: <uzmnozrcb.fsf@gnu.org>
> * 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.
From: Coby Beck
Subject: Re: listo-to-hash
Date: 
Message-ID: <j4Gif.131598$y_1.51677@edtnps89>
"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")
From: Pascal Costanza
Subject: Re: listo-to-hash
Date: 
Message-ID: <3v0o2dF13hqjmU1@individual.net>
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/
From: Asbjørn Bjørnstad
Subject: Re: listo-to-hash
Date: 
Message-ID: <5oacfoiwh8.fsf@kaksi.ifi.uio.no>
"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
From: Asbjørn Bjørnstad
Subject: Re: listo-to-hash
Date: 
Message-ID: <5o3blgiw5l.fsf@kaksi.ifi.uio.no>
···@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
From: Pascal Bourguignon
Subject: Re: listo-to-hash
Date: 
Message-ID: <8764qcrbh7.fsf@thalassa.informatimago.com>
"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
From: javuchi
Subject: Re: listo-to-hash
Date: 
Message-ID: <1133212128.223425.233270@g47g2000cwa.googlegroups.com>
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.
From: Pascal Bourguignon
Subject: Re: listo-to-hash
Date: 
Message-ID: <87hd9wov28.fsf@thalassa.informatimago.com>
"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------
From: javuchi
Subject: Re: listo-to-hash
Date: 
Message-ID: <1133307920.283816.292560@g47g2000cwa.googlegroups.com>
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)))
From: Thomas A. Russ
Subject: Re: listo-to-hash
Date: 
Message-ID: <ymihd9votjj.fsf@sevak.isi.edu>
"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
From: Pascal Bourguignon
Subject: Re: listo-to-hash
Date: 
Message-ID: <878xv6n03n.fsf@thalassa.informatimago.com>
"javuchi" <·······@gmail.com> writes:
> ;devuelve el valor de un hash dentro de otro hash:
> (defmacro re-gethash (key1 key2 hash)
>   `(gethash ,key2 (gethash ,key1 ,hash)))

Which recalls me a generalized "field" function I wrote a month ago,
which would let you write:

   (-> hash key1 key)

but also:

(defstruct demo field)

(-> (hash 'k1 (list 1 (vector 0 1 2 (make-demo :field (hash 'kn "Hi")))))
    'k1 1 3 'demo-field 'kn)

--> "Hi"


;; ----------------------------------

(defun length<= (length list)
  (or (zerop length) (and list (length<= (1- length) (cdr list)))))

(defun -> (object &rest path)
  (cond
    ((null path) object)
    ((arrayp object)
     (let ((d (array-rank object)))
       (if (length<= d path)
           (apply (function ->)
                  (apply (function aref) object (subseq path 0 d))
                  (nthcdr d path))
           (make-array                  ; let's get a slice
            (nthcdr (length path) (array-dimensions object))
            :displaced-to object
            :displaced-index-offset
            (apply (function array-row-major-index) object
                   (append path (make-list (- d (length path))
                                           :initial-element 0)))
            :element-type (array-element-type object)))))
    ((and (listp object)
          (every (function consp) object)
          (not (integerp (first path))))
     (apply (function ->) (cdr (assoc (first path) object)) (rest path)))
    ((listp object)
     (apply (function ->) (elt object (first path)) (rest path)))
    ((hash-table-p object)
     (apply (function ->) (gethash (first path) object) (rest path)))
    ((typep object '(or structure-object standard-object))
     (apply (function ->) (funcall (first path) object) (rest path)))
    ((or (functionp (first path))
         (and (symbolp (first path)) (fboundp (first path))))
     (apply (function ->) (funcall (first path) object) (rest path)))
    (t (error "This is not a compound object: ~S" object))))


;; Tests: 

(defstruct s h l a s)
(defclass c () ((f :accessor f :initarg :f) (g :accessor g :initarg :g)))

(let* ((h (make-hash-table))
       (s (make-instance 'c
            :f (list (make-s :h h :l '((:a . 1) (:b . 2) (:c . 3))
                             :a #2A((1 2) (3 4)) :s "Hello")
                     (make-s :h h :l '((:a . 10) (:b . 20) (:c . 30))
                             :a #2A((1 2 3) (4 5 6) (7 8 9)) :s "world"))
            :g '((* 2 x x) (* 4 x) 3))))
  (setf (gethash 'x h) #(a b c)
        (gethash 'y h) #(d e f))
  (with-input-from-string (stream "Iteration objects structures conditions")
    (values
     (-> #2A((1 2 3) (4 5 6) (7 8 9)) 1 2)
     (-> #2A((1 2 3) (4 5 6) (7 8 9)) 1)
     (-> h 'x)
     (-> 'h 'symbol-name)
     (-> "Hello" 1)
     ;; (-> "Hello" 'string-upcase) ; not yet
     (-> :cl-user 'package-nicknames)
     (-> :cl-user 'package-nicknames 1)
     (-> :cl-user 'package-nicknames 1 0)
     (-> '((:a . 10) (:b . 20) (:c . 30)) 1)
     (-> '((:a . 10) (:b . 20) (:c . 30)) :b)
     ;; (-> '((:a . 10) (:b . 20) (:c . 30)) 'cdr 'cdr 'car) ; not yet
     ;; (-> '(:a 10 :b 20 :c 30) 'cdr 'cdr 'car) ; not yet
     (-> s 'f 0 's-h 'y 2)
     (-> s 'f 1 's-l 1)
     (-> s 'f 0 's-l :b '1+ (function sin))
     (-> s 'f 0 's-a 1)
     ;;(-> s 'f 0 's-a 1 (lambda (v) (coerce v 'list))) ; not yet
     (-> stream 'read 'string-downcase)
     (-> stream 'read 'string-upcase)
     )))

;; To be implemented: a corresponding defsetf.
;; TODO:  change the order of some tests: (-> (list 1 2 3) 'second) should work.





-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

"Klingon function calls do not have "parameters" -- they have
"arguments" and they ALWAYS WIN THEM."