Hash to list?

34 views
Skip to first unread message

Ola Rinta-Koski

unread,
Dec 9, 1999, 3:00:00 AM12/9/99
to
Is there an easier way to convert a hash table into a list than
this? If not easier, how about an alternative where it could be done
already in the LET declaration?

(let (hash-list)
(maphash #'(lambda (key val)
(push val hash-list))
hash-table))
--
Do you agree that it takes over seven minutes for sunlight to get to Earth?

Hannu Koivisto

unread,
Dec 9, 1999, 3:00:00 AM12/9/99
to
Ola Rinta-Koski <ola.rin...@iki.fi.no.spam.today> writes:

| Is there an easier way to convert a hash table into a list than

Values of a hash table, based on your example, right?

| this? If not easier, how about an alternative where it could be done
| already in the LET declaration?

(let ((value-list (loop for i being each hash-value of hash-table
collect i)))
...)

Is this what you had in mind?

--
Hannu

Ola Rinta-Koski

unread,
Dec 9, 1999, 3:00:00 AM12/9/99
to
Hannu Koivisto <az...@iki.fi.ns> writes:
> Ola Rinta-Koski <ola.rin...@iki.fi.no.spam.today> writes:
> | Is there an easier way to convert a hash table into a list than

> Values of a hash table, based on your example, right?

Yes, sorry for not being more clear.

> | this? If not easier, how about an alternative where it could be done
> | already in the LET declaration?
>
> (let ((value-list (loop for i being each hash-value of hash-table
> collect i)))
> ...)
>
> Is this what you had in mind?

Indeed, thanks a lot! I am not too fond of the LOOP macro in
general, but if it does the job at least somewhat readably, then I'm
willing to tolerate it, as in this case.
--
Get this: Do they want to be the first on their block to dress in
revealing lingerie in the foreseeable future, please?

Frode Vatvedt Fjeld

unread,
Dec 9, 1999, 3:00:00 AM12/9/99
to
Ola Rinta-Koski <ola.rin...@iki.fi.no.spam.today> writes:

> I am not too fond of the LOOP macro in general, but if it does the
> job at least somewhat readably, then I'm willing to tolerate it, as
> in this case.

What job is that? What do you mean LOOP gives you here other than some
(quite horrible, IMHO) artificially sweetened syntax of exactly the
same as your LET-form?

If you for some reason don't want to see the temporary collecting
variable, how about wrapping it in a special purpose macro, like this:

(defmacro hash-table-list (hash-table)
(let ((temp-list-name (gensym)))
`(let (,temp-list-name)


(maphash #'(lambda (key val)

(push val ,temp-list-name))
,hash-table)
,temp-list-name)))

--
Frode Vatvedt Fjeld

Tim Bradshaw

unread,
Dec 9, 1999, 3:00:00 AM12/9/99
to
* Ola Rinta-Koski wrote:

> (let (hash-list)


> (maphash #'(lambda (key val)

> (push val hash-list))
> hash-table))

If I was seriously averse to the LOOP-way of doing it, I'd do this:

(collecting


(maphash #'(lambda (key val)

(declare (ignore key))
(collect val))
table))

Where COLLECTING is defined as (there are other versions of this macro
around, this is mine. I apologise for the style, I wrote it a long
time ago):

(defmacro collecting (&body forms)
;; Collect some random stuff into a list by keeping a tail-pointer
;; to it, return the collected list. No real point in using
;; gensyms, although one probably should on principle.
"Collect things into a list forwards. Within the body of this macro
The form `(COLLECT THING)' will collect THING into the list returned by
COLLECTING. Uses a tail pointer -> efficient."
(let (($resnam$ (gensym)) ($tail$ (gensym)) ($thing$ (gensym)))
`(let
(,$resnam$ ,$tail$)
(macrolet
((collect
(thing)
;; Collect returns the thing it's collecting
`(let ((,',$thing$ ,thing))
(if ,',$resnam$
(setf (cdr ,',$tail$)
(setf ,',$tail$ (list ,',$thing$)))
(setf ,',$resnam$
(setf ,',$tail$ (list ,',$thing$))))
,',$thing$)))
,@forms)
,$resnam$)))

Ola Rinta-Koski

unread,
Dec 10, 1999, 3:00:00 AM12/10/99
to
Frode Vatvedt Fjeld <fro...@acm.org> writes:
> Ola Rinta-Koski <ola.rin...@iki.fi.no.spam.today> writes:
> > I am not too fond of the LOOP macro in general, but if it does the
> > job at least somewhat readably, then I'm willing to tolerate it, as
> > in this case.

> What job is that? What do you mean LOOP gives you here other than some
> (quite horrible, IMHO) artificially sweetened syntax of exactly the
> same as your LET-form?

It is, I think, a bit more readable. Neither is particularly elegant.
--
Did you ever wonder why the KGB is out to get him?

Erik Naggum

unread,
Dec 10, 1999, 3:00:00 AM12/10/99
to
* Frode Vatvedt Fjeld <fro...@acm.org>

| What do you mean LOOP gives you here other than some (quite horrible,
| IMHO) artificially sweetened syntax of exactly the same as your LET-form?

I'm curious about all this energy spent hating LOOP and reinventing silly
wheels, not so much because I think LOOP is a good solution to a problem
that does not seem to have any better solutions, but because I think it
shows how the need for patterns might evolve. all the time people whine
about LOOP, they produce a _lot_ of code to supplant simple expressions.

for some bizarre reasons that have nothing to do with just getting the
job done or correctness or speed of execution or any of the numerous
other reasons that are actually valid concerns for rewriting code, LOOP
forms just _have_ to be rewritten, whined about, and detested. offering
to take its place is just a bunch of _new_ weirdness that the author is
sure looks a lot better, but it involves the pattern argument in a strong
sense: the author can read his own "pattern-oriented" code and claims,
directly or indirectly, not to be able to read LOOP forms. this seems
very strange to me, but I have the same reaction to the IF* macro: if I
can't macroexpand this abomination into the COND clauses it "really is",
my head just hurts looking at it, so something I should be able to see is
at work here. yet I don't see it.

what is it about some forms of expression that cry out for rewrites and
which encourages us to start over with something that somebody else must
have thought was much worse than their cure? why is our cure better?

this leads to my question: is the pattern business just a means of saying
"it's OK to retch at this code, but don't rewrite it -- it's the best we
have around", sort of like saying that all the other cures that people
ache to deply will look no better than the pattern-oriented solution?

(yeah, yeah, I'm aware that patterns are supposed to be abstract, but
they wind up being implemented by duplicated, verbose code, regardless.)

#:Erik

Tim Bradshaw

unread,
Dec 10, 1999, 3:00:00 AM12/10/99
to
* Erik Naggum wrote:
> for some bizarre reasons that have nothing to do with just getting the
> job done or correctness or speed of execution or any of the numerous
> other reasons that are actually valid concerns for rewriting code, LOOP
> forms just _have_ to be rewritten, whined about, and detested. offering
> to take its place is just a bunch of _new_ weirdness that the author is
> sure looks a lot better, but it involves the pattern argument in a strong
> sense: the author can read his own "pattern-oriented" code and claims,
> directly or indirectly, not to be able to read LOOP forms.

I wasn't one of those claiming not to be able to read LOOP, but my
alternative COLLECTING thing is something I use a fair amount in
places where really something is going on that LOOP won't do. Typical
examples are where I'm walking recursively over some arbitrary data
structure and just occasionally need to collect some interesting
thing. Whether the trick of building the list forward is worth it
(over the more usual push + nreverse type solution) is another
question.

The other case is this kind of thing:

(collecting
(big-recursion object #'(lambda (o) (collect o))))

Where BIG-RECURSION does (funcall #'collect thing) at various points.
Again, this may not get you anything over push/nreverse but it does do
things that LOOP doesn't.

I'd never actually use COLLECTING in the simple loop case though: I
*like* LOOP!

--tim

Erann Gat

unread,
Dec 10, 1999, 3:00:00 AM12/10/99
to
In article <315382870...@naggum.no>, Erik Naggum <er...@naggum.no> wrote:

> for some bizarre reasons that have nothing to do with just getting the
> job done or correctness or speed of execution or any of the numerous
> other reasons that are actually valid concerns for rewriting code, LOOP
> forms just _have_ to be rewritten, whined about, and detested. offering
> to take its place is just a bunch of _new_ weirdness that the author is
> sure looks a lot better

Ironically, it may be a fundamental feature of Lisp that encourages
this sort of behavior. One of the great strengths of the language is
that it makes it easy to do incremental compilation, which results in
short debug cycles, which makes it very tempting to do just one more
iteration to get it Right with a capital R. Since every iteration is
cheap it's very insidious -- you can fritter away the whole day on
some useless piece of art before you realize it. C/C++/Java programmers
never get caught in this because they know a priori that if they even
*try* to make an existing thing better it will take them the whole day,
so they don't start. Instead, they accept the existing code base cruft
and all and move on. This is why they win.

Erann Gat
g...@jpl.nasa.gov

Frode Vatvedt Fjeld

unread,
Dec 12, 1999, 3:00:00 AM12/12/99
to
Erik Naggum <er...@naggum.no> writes:

> for some bizarre reasons that have nothing to do with just getting
> the job done or correctness or speed of execution or any of the
> numerous other reasons that are actually valid concerns for
> rewriting code, LOOP forms just _have_ to be rewritten, whined
> about, and detested.

Well, I disagree with you here. I think "(hash-table-list x)" is a lot
more readable (and not the least bit weird) than the loop expression,
which I find about equally readable to the "plain" lisp form. And,
more importantly, the time-to-convince-me-it's-correct factor is also
much better.

I'm a bit confused: My impression after reading OnLisp and other books
is that I'm encouraged to write and use precisely this kind of
"utilities". Do you disagree with this?

--
Frode Vatvedt Fjeld

Jeff Dalton

unread,
Dec 13, 1999, 3:00:00 AM12/13/99
to
Tim Bradshaw <t...@cley.com> writes:

> * Ola Rinta-Koski wrote:
>
> > (let (hash-list)
> > (maphash #'(lambda (key val)
> > (push val hash-list))
> > hash-table))
>
> If I was seriously averse to the LOOP-way of doing it, I'd do this:
>
> (collecting
> (maphash #'(lambda (key val)
> (declare (ignore key))
> (collect val))
> table))

Humm. I just define a function (hash-table-values ht) and use that.
Then whatever ugly or elegant code I use to get the actual values is
just written out once.

By the way, I think it would be useful to collect some of these
lists of different ways to do things, add some discussion of the
ways, and make them available to people wanting to learn Lisp
(e.g. by putting them on Web pages).

So far no one has suggested using with-hash-table-iterator.

-- j

Jeff Dalton

unread,
Dec 13, 1999, 3:00:00 AM12/13/99
to
Erik Naggum <er...@naggum.no> writes:

> I'm curious about all this energy spent hating LOOP and reinventing silly
> wheels, not so much because I think LOOP is a good solution to a problem
> that does not seem to have any better solutions, but because I think it
> shows how the need for patterns might evolve. all the time people whine
> about LOOP, they produce a _lot_ of code to supplant simple expressions.

A curious thing is that when books try to say what's bad about LOOP
(and I think even one of Graham's does this), they give some of the
least clear examples -- even though in some cases, LOOP is as clear
as anything and clearer than most alternatives. For instance DOTIMES
handles some things quite well; but if you depart from the 0 upto n-1
case, LOOP is usually better.

-- jd

Erik Naggum

unread,
Dec 13, 1999, 3:00:00 AM12/13/99
to
* Frode Vatvedt Fjeld <fro...@acm.org>
| Well, I disagree with you here. I think "(hash-table-list x)" is a lot
| more readable (and not the least bit weird) than the loop expression,
| which I find about equally readable to the "plain" lisp form. And, more
| importantly, the time-to-convince-me-it's-correct factor is also much
| better.

well, I would _not_ understand what a function called HASH-TABLE-LIST was
doing without reading its documentation or source code, and that I would
have to repeat every time I found it in a new package or project. the
reason is quite simple: there are so many lists that could be generated
from hash tables that none of them merit being the only one thus named.

| I'm a bit confused: My impression after reading OnLisp and other books is
| that I'm encouraged to write and use precisely this kind of "utilities".
| Do you disagree with this?

actually, yes. naming utility functions is a very hard problem, and they
should therefore not be multiplied lightly. when there is clearly a
diversity of needs, it is better to be a little more verbose than to
pollute the name space with thousands of functions. the C++ way to do
this is to talk about patterns to discourage people from creating tons of
one-call functions. the Lisp way is to create sub-languages and macros
that cover _multiple_ needs well. I actually think LOOP succeeds in
this, but it suffers in my view from not creating an environment with
local macros instead of the positional "lexical keyword" stuff that I
have come to dislike in other languages.

#:Erik

Christopher R. Barry

unread,
Dec 13, 1999, 3:00:00 AM12/13/99
to
Erik Naggum <er...@naggum.no> writes:

> | I'm a bit confused: My impression after reading OnLisp and other books is
> | that I'm encouraged to write and use precisely this kind of "utilities".
> | Do you disagree with this?
>
> actually, yes. naming utility functions is a very hard problem, and they
> should therefore not be multiplied lightly. when there is clearly a
> diversity of needs, it is better to be a little more verbose than to
> pollute the name space with thousands of functions.

I strongly agree here. The problem is that Graham's books and Norvig's
books do this for like every single program and encourage it. I think
it just makes reading the code ten times more of a PITA as you have to
set up a tags table and constantly peck away at M-. so you can follow
the abstraction's function-calls back to the source and memorize what
they do before continuing. Here are some of the "useful abstractions"
you can find in both the PAIP and AI:AMA code from Norvig, for example:

(defun length>1 (list)
"Is this a list of 2 or more elements?"
(and (consp list) (cdr list)))

(defun length=1 (list)
"Is this a list of exactly one element?"
(and (consp list) (null (cdr list))))

(defun starts-with (list element)
"Is this a list that starts with the given element?"
(and (consp list) (eq (first list) element)))

(defun last1 (list)
"Return the last element of a list."
(first (last list)))

(defun left-rotate (list)
"Move the first element to the end of the list."
(append (rest list) (list (first list))))

(defun right-rotate (list)
"Move the last element to the front of the list."
(append (last list) (butlast list)))


These kind of things get REALLY ANNOYING after awhile....

Christopher

Jeff Dalton

unread,
Dec 14, 1999, 3:00:00 AM12/14/99
to

I think all of the above are both useful and clear, and I define and
use similar functions myself. If I saw, for instance, the (append
(rest ...) (list (first same...)) in-line, I'd have to take time to
work out what it did, especially if the "..." were replaced by
something nontrivial. Left-rotate, on the other hand, is instantly
recognisable.

Coby Beck

unread,
Dec 14, 1999, 3:00:00 AM12/14/99
to

Christopher R. Barry <cba...@2xtreme.net> wrote in message
news:874sdma...@2xtreme.net...

> Erik Naggum <er...@naggum.no> writes:
>
> > | I'm a bit confused: My impression after reading OnLisp and other books
is
> > | that I'm encouraged to write and use precisely this kind of
"utilities".
> > | Do you disagree with this?
> >
> > actually, yes. naming utility functions is a very hard problem, and
they
> > should therefore not be multiplied lightly. when there is clearly a
> > diversity of needs, it is better to be a little more verbose than to
> > pollute the name space with thousands of functions.
>
> I strongly agree here. The problem is that Graham's books and Norvig's
> books do this for like every single program and encourage it. I think
> it just makes reading the code ten times more of a PITA as you have to
> set up a tags table and constantly peck away at M-. so you can follow
> the abstraction's function-calls back to the source and memorize what
> they do before continuing.

Yes, the balance is often lost. It is so easy (and fun) to personalize lisp
that if you're not careful, no one but the author will know what is going
on. AIMA was very difficult to follow because of that.

>Here are some of the "useful abstractions"
> you can find in both the PAIP and AI:AMA code from Norvig, for example:
>
> (defun length>1 (list)
> "Is this a list of 2 or more elements?"
> (and (consp list) (cdr list)))
>
> (defun length=1 (list)
> "Is this a list of exactly one element?"
> (and (consp list) (null (cdr list))))
>
> (defun starts-with (list element)
> "Is this a list that starts with the given element?"
> (and (consp list) (eq (first list) element)))
>
> (defun last1 (list)
> "Return the last element of a list."
> (first (last list)))
>
> (defun left-rotate (list)
> "Move the first element to the end of the list."
> (append (rest list) (list (first list))))
>
> (defun right-rotate (list)
> "Move the last element to the front of the list."
> (append (last list) (butlast list)))
>
>
> These kind of things get REALLY ANNOYING after awhile....
>

> Christopher

Interesting. While I agreed with your general point i think your choice of
examples weakens it. All of the above utilities are very clearly and
appropriately named and supply basic functionality that is very commonly
needed. (well, with the possible exception of the rotate functions, which
aside from school exercises, i have not ever needed yet)

There is definately a worthwhile gain in readabiltiy and writability here.
I frequently use a number of those utilities plus a dozen or so similar of
my own.

The points made above still stand, however. *Everything* in moderation.

Coby


Coby Beck

unread,
Dec 14, 1999, 3:00:00 AM12/14/99
to
(reply not directed at quoted author)

> In article <315382870...@naggum.no>, Erik Naggum <er...@naggum.no>
wrote:
>
> > for some bizarre reasons that have nothing to do with just getting the
> > job done or correctness or speed of execution or any of the numerous
> > other reasons that are actually valid concerns for rewriting code,
LOOP
> > forms just _have_ to be rewritten, whined about, and detested.
offering
> > to take its place is just a bunch of _new_ weirdness that the author
is
> > sure looks a lot better

Well, I have yet to see anyone else voice this opinion, but it is mine...

I LIKE LOOP!! : )

Coby


Robert Monfera

unread,
Dec 14, 1999, 3:00:00 AM12/14/99
to

Jeff Dalton wrote:

> cba...@2xtreme.net (Christopher R. Barry) writes:

> > Here are some of the "useful abstractions"
> > you can find in both the PAIP and AI:AMA code from Norvig, for example:

[examples elided]

> > These kind of things get REALLY ANNOYING after awhile....

> I think all of the above are both useful and clear, and I define and


> use similar functions myself. If I saw, for instance, the (append
> (rest ...) (list (first same...)) in-line, I'd have to take time to
> work out what it did, especially if the "..." were replaced by
> something nontrivial. Left-rotate, on the other hand, is instantly
> recognisable.

It is certainly true that it takes some time to analyze the snippet. I
usually define every utility in a FLET first, or more likely just use an
extra line in a LET or LOOP environment to _name_ some interim result so
as not to have to recognise and analyse the snippet. I make it a global
function when it becomes a PITA to write it several times, but I found I
waste less time this way than finding out the name of a globally defined
utility and adjusting it when I want to use it the second time. It's
easier for me to make the proper generalization after I have gotten my
hands dirty at some cases.

It could lead to better optimization (e.g., inlining, type assertions or
something even more drastic), but it's another story.

Speaking of FLETs and LETs: what's the best way of testing them?
Sometimes I just copy the snippet and put a DEFUN or SETF wrapper around
it, but there has to be a better way. As for LET statements, my
debugger shows the bindings for some, but not necessarily all lexical
variables (at (debug 3) of course), but I can not invoke a FLET.

Here's a wishlist: at (debug 3), be able to

- access _all_ lexical bindings
- invoke local functions
- restart and continue with every stack frame

Maybe such an animal exists, I just haven't found or tamed it.
(Luckily, I am not in the debugger too often :-) What are the best
practices?

Thanks,

Robert

Christopher R. Barry

unread,
Dec 15, 1999, 3:00:00 AM12/15/99
to
Robert Monfera <mon...@fisec.com> writes:

[...]

> Speaking of FLETs and LETs: what's the best way of testing them?
> Sometimes I just copy the snippet and put a DEFUN or SETF wrapper around
> it, but there has to be a better way.

You can pass them up as upward closures:

> (flet ((some-function ()
'blah))
#'some-function)
=> #<FUNCTION SOME-FUNCTION blah blah ...>

> As for LET statements, my debugger shows the bindings for some, but
> not necessarily all lexical variables (at (debug 3) of course), but
> I can not invoke a FLET.
>
> Here's a wishlist: at (debug 3), be able to
>
> - access _all_ lexical bindings
> - invoke local functions
> - restart and continue with every stack frame
>
> Maybe such an animal exists, I just haven't found or tamed it.

Looks like you want a Symbolics. You can do all of the above. To
invoke a local function though, you need to be in a debugger stack
frame where the local function is in scope. (There's not some magic
function of macro you can use to say "give me the flet-function named
FOO in global function BAR or anything like that, if that's what you
were thinking of.) In the Genera debugger, when you evaluate a form it
is by default evaluated in the environment of the program you are
debugging. For example, if you do:

> (flet ((add (x y)
(+ x y)))
(let ((a 1)
(b 2))
(break)))

You'll be in the debugger and you can then do:

-> (add 3 4)
=> 7

-> (add a b)
=> 3

> (Luckily, I am not in the debugger too often :-) What are the best
> practices?

When not using Genera I generally use debug-print macros instead of
the debugger, though Allegro CL has got a pretty good
debugger. (LispWorks seems to as well from browsing the doc, but I
haven't used it much.) With (DEBUG 3) and (SPEED 0) you still don't
seem to get all of the locals with their names (ACL 5.0.1, Linux/X86)
many times, but debug-printing will always give you that info....

Christopher

Christopher R. Barry

unread,
Dec 15, 1999, 3:00:00 AM12/15/99
to
Jeff Dalton <je...@todday.aiai.ed.ac.uk> writes:

[...]

> I think all of the above are both useful and clear, and I define and
> use similar functions myself. If I saw, for instance, the (append
> (rest ...) (list (first same...)) in-line, I'd have to take time to
> work out what it did, especially if the "..." were replaced by
> something nontrivial.

This is what commenting is for. If you use the same procedure A LOT
throughout a program then sure, make a global function. Else, I think
flets or just coding in place with commenting is better. Note that
even with INLINE declarations (which a lot of people seem to be saying
lately that most CL's don't honor...), there are performance impacts
to be wary of when using these small global functions in certain
implementations and contexts.

For example, I once wrote a program that would read a large file in a
line at a time and do some pretty compute-intensive stuff. Part of it
looked like this:

(loop for start = (position-if #'(lambda (char) <blah>) ...) ...)

I later realized that I had duplicated the (LAMBDA (CHAR) <blah>)
elsewhere so I created a global function and replaced both the lambdas
with it and performance went from around 8 seconds to 12 seconds!
(This was with Allegro CL 5.0.1 Linux/X86 if anyone cares. I didn't
try with CMU CL but should of....) This was a real eye-opener I
thought. (And needless to say I left the lambdas as they were.)

Christopher

Jason Trenouth

unread,
Dec 15, 1999, 3:00:00 AM12/15/99
to
On 10 Dec 1999 15:31:48 +0000, Erik Naggum <er...@naggum.no> wrote:

> * Frode Vatvedt Fjeld <fro...@acm.org>

> | What do you mean LOOP gives you here other than some (quite horrible,
> | IMHO) artificially sweetened syntax of exactly the same as your LET-form?
>

> I'm curious about all this energy spent hating LOOP and reinventing silly
> wheels, not so much because I think LOOP is a good solution to a problem
> that does not seem to have any better solutions, but because I think it
> shows how the need for patterns might evolve. all the time people whine
> about LOOP, they produce a _lot_ of code to supplant simple expressions.

I'm pretty sure this will fuel passions and prejudices about LOOP, but FTR
LispWorks' SQL interface extends LOOP to work over database query results, e.g.

(loop for (name age . rest)
being each record in "select * from person"
initially (format t "~%~20A ~5A" "NAME" "AGE")
count id into people
sum age into total
do (format t "~%~20A ~5D" name age)
finally (format t "~2%AVERAGE AGE = ~D" (/ total people)))

And voila, LOOP as database reporting language! :-j

__Jason

PS The macroexpansion uses a database cursor to iterate over the results rather
than secretly cons'ing a list and then iterating over that.

Marco Antoniotti

unread,
Dec 15, 1999, 3:00:00 AM12/15/99
to

Jason Trenouth <ja...@harlequin.com> writes:

> On 10 Dec 1999 15:31:48 +0000, Erik Naggum <er...@naggum.no> wrote:
>
> > * Frode Vatvedt Fjeld <fro...@acm.org>
> > | What do you mean LOOP gives you here other than some (quite horrible,
> > | IMHO) artificially sweetened syntax of exactly the same as your LET-form?
> >
> > I'm curious about all this energy spent hating LOOP and reinventing silly
> > wheels, not so much because I think LOOP is a good solution to a problem
> > that does not seem to have any better solutions, but because I think it
> > shows how the need for patterns might evolve. all the time people whine
> > about LOOP, they produce a _lot_ of code to supplant simple expressions.
>
> I'm pretty sure this will fuel passions and prejudices about LOOP, but FTR
> LispWorks' SQL interface extends LOOP to work over database query results, e.g.
>
> (loop for (name age . rest)
> being each record in "select * from person"
> initially (format t "~%~20A ~5A" "NAME" "AGE")
> count id into people
> sum age into total
> do (format t "~%~20A ~5D" name age)
> finally (format t "~2%AVERAGE AGE = ~D" (/ total people)))
>
> And voila, LOOP as database reporting language! :-j

This is cool. What is the complete syntax of the extension? Is it
just

each RECORD in <SQL-QUERY-AS-STRING>
and
each RECORD in <SQL-QUERY-AS-SEXP>

?

Cheers

--
Marco Antoniotti ===========================================
PARADES, Via San Pantaleo 66, I-00186 Rome, ITALY
tel. +39 - 06 68 10 03 17, fax. +39 - 06 68 80 79 26
http://www.parades.rm.cnr.it/~marcoxa

Jason Trenouth

unread,
Dec 15, 1999, 3:00:00 AM12/15/99
to
On 15 Dec 1999 14:46:40 +0100, Marco Antoniotti <mar...@parades.rm.cnr.it>
wrote:

> > (loop for (name age . rest)
> > being each record in "select * from person"
> > initially (format t "~%~20A ~5A" "NAME" "AGE")
> > count id into people
> > sum age into total
> > do (format t "~%~20A ~5D" name age)
> > finally (format t "~2%AVERAGE AGE = ~D" (/ total people)))
> >
> > And voila, LOOP as database reporting language! :-j
>
> This is cool. What is the complete syntax of the extension? Is it
> just
>
> each RECORD in <SQL-QUERY-AS-STRING>
> and
> each RECORD in <SQL-QUERY-AS-SEXP>
>
> ?
>
> Cheers

The syntax is defined as:

{for|as} var [type-spec] being
{the|each}{tuples|tuple}
{in|of} query-expression

(but I think you can use "record" as well as "tuple"). And "query-expression"
can use the symbolic-SQL expression syntax.

Common SQL is described here:


http://www.harlequin.com/education/books/lispworks/lww/lwuser/LWUG_142.HTM#HEADING142-0

And iteration in particular is described here:


http://www.harlequin.com/education/books/lispworks/lww/lwuser/LWUG_156.HTM#HEADING156-0

__Jason

Peter Norvig

unread,
Dec 15, 1999, 3:00:00 AM12/15/99
to Christopher R. Barry
Taste varies here. I prefer

(length>1 (some long expression))

because I *think* its easier to read than

(let ((temp (some long expression)))
(and (consp temp) (cdr temp)))

and because I *know* it is more efficient than what would be used otherwise:

(> (length (some long expression)) 1)

One thing that makes tastes vary is the environment in which one is
served. I'm used to an editing environment in which, when the cursor is
placed at the "_" in

(length>1 _ (some long expression))

one automatically sees "length>1 (list): Is this a list of 2 or more elements?"
in the mode line, with no need to have to hit M-., no need to move about
in different buffers, and with the tags table set up for you
automatically. I agree that reading paper printouts can be hard, but
for online reading, I never had a problem with this style, for my code
or for others.

"Christopher R. Barry" wrote:
>
> Erik Naggum <er...@naggum.no> writes:
>
> > | I'm a bit confused: My impression after reading OnLisp and other books is
> > | that I'm encouraged to write and use precisely this kind of "utilities".
> > | Do you disagree with this?
> >
> > actually, yes. naming utility functions is a very hard problem, and they
> > should therefore not be multiplied lightly. when there is clearly a
> > diversity of needs, it is better to be a little more verbose than to
> > pollute the name space with thousands of functions.
>
> I strongly agree here. The problem is that Graham's books and Norvig's
> books do this for like every single program and encourage it. I think
> it just makes reading the code ten times more of a PITA as you have to
> set up a tags table and constantly peck away at M-. so you can follow
> the abstraction's function-calls back to the source and memorize what

> they do before continuing. Here are some of the "useful abstractions"


> you can find in both the PAIP and AI:AMA code from Norvig, for example:
>

> (defun length>1 (list)
> "Is this a list of 2 or more elements?"
> (and (consp list) (cdr list)))
>

> ...
> Christopher

Tom Breton

unread,
Dec 15, 1999, 3:00:00 AM12/15/99
to
Jason Trenouth <ja...@harlequin.com> writes:

> On 10 Dec 1999 15:31:48 +0000, Erik Naggum <er...@naggum.no> wrote:

>
> I'm pretty sure this will fuel passions and prejudices about LOOP, but FTR
> LispWorks' SQL interface extends LOOP to work over database query results, e.g.

Well, I'm not sure that fuels passions. }:) My main objections to
LOOP (which, NB, I do use but prefer other forms whenever practical)
are the un-Lisp syntax and the overloading of familiar symbols like if
and and. Make it look and behave like Lisp and I'd like it.

If it easily integrates database handling, well, good.

--
Tom Breton, http://world.std.com/~tob
Not using "gh" since 1997. http://world.std.com/~tob/ugh-free.html

Ola Rinta-Koski

unread,
Dec 16, 1999, 3:00:00 AM12/16/99
to
Tom Breton <t...@world.std.com> writes:
> Well, I'm not sure that fuels passions. }:) My main objections to
> LOOP (which, NB, I do use but prefer other forms whenever practical)
> are the un-Lisp syntax and the overloading of familiar symbols like if
> and and. Make it look and behave like Lisp and I'd like it.

My sentiments exactly. At least I have to shift gears in my brain
whenever going from Lisp mode to LOOP mode, and I'd rather not.
--
Someone told me there are people starving in Honduras! I'm out of
work...I could go into shock absorbers...or SCUBA GEAR!!

Marc Battyani

unread,
Dec 16, 1999, 3:00:00 AM12/16/99
to
If you want an more lispy alternative to loop you should look at iterate.
I'm considering using it. So if anybody has good or bad experiences with it,
please tell us.

Marc Battyani

Tom Breton <t...@world.std.com> wrote in message
news:m3vh5z3...@world.std.com...
.../...


> Well, I'm not sure that fuels passions. }:) My main objections to
> LOOP (which, NB, I do use but prefer other forms whenever practical)
> are the un-Lisp syntax and the overloading of familiar symbols like if
> and and. Make it look and behave like Lisp and I'd like it.

.../...

Robert Monfera

unread,
Dec 16, 1999, 3:00:00 AM12/16/99
to

Marc,

Have you looked at SERIES yet?

Robert

Jeff Dalton

unread,
Dec 17, 1999, 3:00:00 AM12/17/99
to
cba...@2xtreme.net (Christopher R. Barry) writes:

> Jeff Dalton <je...@todday.aiai.ed.ac.uk> writes:
>
> [...]
>
> > I think all of the above are both useful and clear, and I define and
> > use similar functions myself. If I saw, for instance, the (append
> > (rest ...) (list (first same...)) in-line, I'd have to take time to
> > work out what it did, especially if the "..." were replaced by
> > something nontrivial.
>
> This is what commenting is for.

No it isn't. It is not good practice to write less clear code and
then throw in a comment (which might be out of date or just plain
wrong) rather than write clearer code.

> If you use the same procedure A LOT
> throughout a program then sure, make a global function. Else, I think
> flets or just coding in place with commenting is better. Note that
> even with INLINE declarations (which a lot of people seem to be saying
> lately that most CL's don't honor...), there are performance impacts
> to be wary of when using these small global functions in certain
> implementations and contexts.

What I do, so far as optimizations are concerned, is to write code
that is likely to be reasonably efficient in most implementations
that aim for efficiency, and then if the resulting performance is
not what I'd like, I perform experiments to see where the time
is being spent, etc -- all the usual things one does to find ways
to increase efficiency. If a particular problem keeps coming
up, I change my idea of what code is "likely to be reasonably
efficient".

I'm not going to avoid small global functions unless they turn out to
be a problem of that sort; and if that happens I'll resort to the old
defsubst technique (since CL now has compiler macros). (That is, I'll
use a macro -- defsubst -- that defines a function together with a
compiler macro.)

Anyway, if there's a commonly-used idiomatic code form that is
sufficiently complex to not be immediately clear and that I can
capture in a function that has a clear enough name, I do so capture
it, because I find that the resulting code is clearer and the
efficiency cost is not great. If in some case it turns out to be too
costly, I can always resort to the tactic of writing it out in line
and adding a comment.

[Note that I say "commonmly used" while you say "A LOT". Anyway,
I think I have a pretty good idea of when something is likely to be
a useful utility and when it's not. Frequency of use is not the
only factor.]

length=1 is a case that could go either way. Instead of (lengh=1 x),
one could write (= (length x) 1), which is quite clear. Unfortunately,
it might well result in a call to length. That "threat" is great
enough that I've defined and used a lengt=1. (Indeed, at least one of
the CLs that I use all the time will definitely emit a call to length.)
The cost of actually calling the function, in Lisps where the INLINE
declaration does not help, is one that I can almost always live with.
(Calls to small "leaf" procedures are handled very efficiently these
days.)

One thing I'd like to be able to do, though, is to write code in which
DEFUNs are wrapped inside a LABELs, so that I can have more than one
global function using the same local utilities. Unfortunately, it can
be tricky to get such constructs to be compiled, and since the
resulting global functions are closed over a non-null lexical env,
efficiency might be a problem. Worse, though, is that in many
implementations such things are tricky to debug.

> For example, I once wrote a program that would read a large file in a
> line at a time and do some pretty compute-intensive stuff. Part of it
> looked like this:
>
> (loop for start = (position-if #'(lambda (char) <blah>) ...) ...)
>
> I later realized that I had duplicated the (LAMBDA (CHAR) <blah>)
> elsewhere so I created a global function and replaced both the lambdas
> with it and performance went from around 8 seconds to 12 seconds!
> (This was with Allegro CL 5.0.1 Linux/X86 if anyone cares. I didn't
> try with CMU CL but should of....) This was a real eye-opener I
> thought. (And needless to say I left the lambdas as they were.)

That sort of thing doesn't surprise me. It's fairly common for
functions such as position-if to be compiled in-line (because
otherwise, given all the keyword parameters and functional arguments
that would have to be funcalled, the results can be pretty
inefficient.) And it's fairly common for lambda-exprs to be
compiled in-line when they're just being called.

Unfortunately, implementations vary quite a bit when it comes to
exactly which built-in functions they compile in-line. mapcar
uually is, but mapcan is less likely to be, for example.

-- jd


Marco Antoniotti

unread,
Dec 18, 1999, 3:00:00 AM12/18/99
to

#+with-noise-ahead

Jeff Dalton <je...@todday.aiai.ed.ac.uk> writes:

> cba...@2xtreme.net (Christopher R. Barry) writes:
> >
> > This is what commenting is for.
>
> No it isn't. It is not good practice to write less clear code and
> then throw in a comment (which might be out of date or just plain
> wrong) rather than write clearer code.

I usually write very dirty code without comments :) It makes the
stuff harder to read and qulifies me as a "real programmer". :)

BTW. I don't eat quiche :)

Jonathan Coupe

unread,
Jan 2, 2000, 3:00:00 AM1/2/00
to

Jeff Dalton <je...@todday.aiai.ed.ac.uk> wrote in message
news:x2r9gqaj...@todday.aiai.ed.ac.uk...

>
> So far no one has suggested using with-hash-table-iterator.
>
> -- j

I use "with-hash-table-iterator" for this job myself. I suspect that one
reason it might be a relatively uncommon method is that most people seem to
reply on Paul Graham's book, and hash-table iterator just barely scrapes a
minimal reference in the appendix - while "loop" is heavily decremented.

Jonathan

Reply all
Reply to author
Forward
0 new messages