Lisp HUG Maillist Archive

gethash abstraction

Hi, I'm relatively new to Lisp... last semester in my classes I had a 
course which involved a good amount of Scheme, and I quite enjoyed it, 
so I thought for my undergrad research I'd try a little something with 
Common Lisp.  The project is on network science, and since I'm a little 
new to the language this might seem like a dumb question.  But then, I 
figure being afraid to ask the dumb questions just makes you look even 
more dumb later on ;-)

I'm using hash tables in a couple places in my project, but I find that 
I'm using a pattern quite often where I first check to see if a hash 
table has a certain key, by checking whether (gethash key myhash) 
returns nil, and then taking some action on that slot in the has table 
depending on whether or not it already exists.

The question I have is, it seems like I end up repeating the same 
gethash expression like two, three, maybe four times in the same 
function or method -- testing it, setting it, modifying it, returning 
it...  This kind of repetition, I gather, is not the Lisp Way ;-) nor is 
it the way I like to program.

However, I tried using a let expression, in a manner similar to the 
following (simplified for clarity):
....
   (let ((loc (gethash key myhash)))
     (if (null loc)
       (setf loc 0)
       (incf loc))) ...
And I'm getting errors.  Errors I don't fully understand, as the 
debugger tends to still mistify me a bit right now.  Like I said, I'm 
new to the language.  But apparently this is not the proper way top 
abstract a reference into a hash table.
  a) I know something's wrong about the above but I don't have the words 
to express what it is.
      Could someone explain it a little?
  b) What's the right way (or at least a way that works) to do what I'm 
getting at?

--ch--


Re: gethash abstraction

>    (let ((loc (gethash key myhash)))
>      (if (null loc)
>        (setf loc 0)
>        (incf loc))) ...

I should note that similar code, by itself, doesn't give me an error.
I suppose that in a bigger setting such situations could arise...

> But apparently this is not the proper way top
> abstract a reference into a hash table.
>   a) I know something's wrong about the above but I don't have the words
> to express what it is.
>       Could someone explain it a little?
>   b) What's the right way (or at least a way that works) to do what I'm
> getting at?

The let establishes a local binding, so setf'ing on it isn't going to do
anything to the actual hashtable. What you might be looking for is
something using "symbol-macrolet", e.g.

(let ((ht (make-hash-table)))
  (symbol-macrolet ((loc (gethash :some-key ht)))
    (if (null loc)
      (setf loc 0)
      (incf loc))))

The idea of what's going on there is that the symbol
loc expands to (gethash :some-key ht). Symbol-macrolet
is also used by "with-slots" with which you might be more
familiar (if you use CLOS). Docs for with-slots are

http://www.lispworks.com/documentation/HyperSpec/Body/m_w_slts.htm

and for symbol-macrolet are

http://www.lispworks.com/documentation/HyperSpec/Body/s_symbol.htm#symbol-macrolet

Of course, beware of the fact that if you use this, you'll be accessing the
hash table at every use, which you don't really need to do if you just want to
read the value once and use it in a few places (without modifying the table).

--
=====================
Joshua Taylor
tayloj@rpi.edu


Re: gethash abstraction

Charles Hoffman wrote:
>
> I'm using hash tables in a couple places in my project, but I find 
> that I'm using a pattern quite often where I first check to see if a 
> hash table has a certain key, by checking whether (gethash key myhash) 
> returns nil, and then taking some action on that slot in the has table 
> depending on whether or not it already exists.
>
> The question I have is, it seems like I end up repeating the same 
> gethash expression like two, three, maybe four times in the same 
> function or method -- testing it, setting it, modifying it, returning 
> it...  This kind of repetition, I gather, is not the Lisp Way ;-) nor 
> is it the way I like to program.
>
> However, I tried using a let expression, in a manner similar to the 
> following (simplified for clarity):
> ...
>   (let ((loc (gethash key myhash)))
>     (if (null loc)
>       (setf loc 0)
>       (incf loc))) ...
gethash takes a third optional argument, see

http://www.lispworks.com/documentation/HyperSpec/Body/f_gethas.htm#gethash

The third arg is to return a default if the value does not exist in the 
hash-table.  The error
you are probably getting is trying to bind NIL to 0, which is not 
allowed in CL.  NIL is
the value returned by (gethash key myhash)

The above code is simply written as

(incf (gethash key myhash -1))

CL-USER 1 > (defparameter myhash (make-hash-table))
MYHASH

CL-USER 2 > (incf (gethash 'key1 myhash -1))
0

CL-USER 3 > (gethash 'key1 myhash)
0
T

CL-USER 4 >

Wade

> And I'm getting errors.  Errors I don't fully understand, as the 
> debugger tends to still mistify me a bit right now.  Like I said, I'm 
> new to the language.  But apparently this is not the proper way top 
> abstract a reference into a hash table.
>  a) I know something's wrong about the above but I don't have the 
> words to express what it is.
>      Could someone explain it a little?
>  b) What's the right way (or at least a way that works) to do what I'm 
> getting at?
>
> --ch--
>
>


Re: gethash abstraction

Hi,

I don't completely understand what you are trying to do from the code  
you give, but note that you can specify default values in a gethash  
form. So for example, the following form automagically does the right  
thing if the key is not in the hashtable:

(defparameter *table* (make-hash-table))

(incf (gethash 'foo *table* 0))

(gethash 'foo *table*) => 1

Pascal

On 10 Apr 2006, at 05:20, Charles Hoffman wrote:

>
> Hi, I'm relatively new to Lisp... last semester in my classes I had  
> a course which involved a good amount of Scheme, and I quite  
> enjoyed it, so I thought for my undergrad research I'd try a little  
> something with Common Lisp.  The project is on network science, and  
> since I'm a little new to the language this might seem like a dumb  
> question.  But then, I figure being afraid to ask the dumb  
> questions just makes you look even more dumb later on ;-)
>
> I'm using hash tables in a couple places in my project, but I find  
> that I'm using a pattern quite often where I first check to see if  
> a hash table has a certain key, by checking whether (gethash key  
> myhash) returns nil, and then taking some action on that slot in  
> the has table depending on whether or not it already exists.
>
> The question I have is, it seems like I end up repeating the same  
> gethash expression like two, three, maybe four times in the same  
> function or method -- testing it, setting it, modifying it,  
> returning it...  This kind of repetition, I gather, is not the Lisp  
> Way ;-) nor is it the way I like to program.
>
> However, I tried using a let expression, in a manner similar to the  
> following (simplified for clarity):
> ...
>   (let ((loc (gethash key myhash)))
>     (if (null loc)
>       (setf loc 0)
>       (incf loc))) ...
> And I'm getting errors.  Errors I don't fully understand, as the  
> debugger tends to still mistify me a bit right now.  Like I said,  
> I'm new to the language.  But apparently this is not the proper way  
> top abstract a reference into a hash table.
>  a) I know something's wrong about the above but I don't have the  
> words to express what it is.
>      Could someone explain it a little?
>  b) What's the right way (or at least a way that works) to do what  
> I'm getting at?
>
> --ch--
>

-- 
Pascal Costanza, mailto:pc@p-cos.net, http://p-cos.net
Vrije Universiteit Brussel, Programming Technology Lab
Pleinlaan 2, B-1050 Brussel, Belgium





Re: gethash abstraction

Charles Hoffman schrieb:
> 
>   (let ((loc (gethash key myhash)))
>     (if (null loc)
>       (setf loc 0)
>       (incf loc))) ...
> And I'm getting errors.

The problem here is that let does not create a reference / shortcut for 
you. What it does is reading the value out of the HT and binding it to 
your variable loc.
Imagine that in the HT cell "key" there is a 7.
This makes your code like this:

(if (null 7)
     (setf 7 0)
     (incf 7))

Obviously you can't change the value of 7.
And as others pointed out:
you should care about the third return value of gethash for your if.
(nth-value 2 (gethash key myhash))

What you want is a literal substitution of LOC with (gethash key myhash)
Probably symbol-macrolet will do what you want.


Regards,
André


Updated at: 2020-12-10 08:48 UTC