Hi!
I am doing a library web app (loan book, return book, add book to
collection etc) for work and decided to try out AllegroServe and
AllegroCache. Normally I would have used SBCL + Hunchentoot + CL-SQL
but on Windows those does not seem to play very well.
This is my first try using CLOS and also something like AllegroCache
instead of a plain SQL database and lists so the first problem I
encountered was sorting the titles:
(defclass title ()
((id :initarg :id :reader id :index :any-
unique)
(name :initarg :name :reader name)
(author :initarg :author :reader author)
(language :initarg :language :reader language)
(owner :initarg :owner :reader owner)
(genre :initarg :genre :reader genre)
(category :initarg :category :reader category :index :any)
(status :initarg :status :reader status :index :any)
(loans :initarg :loans :reader loans :index :any)
(borrowed-by :initarg :borrowed-by :reader borrowed-
by :index :any))
(:metaclass persistent-class))
I came up with this, which works:
(defun my-sort (objects slot-name)
(let ((sort-fn
(cond (;; String slots
(member slot-name '(name author language owner genre
borrowed-by))
#'string<)
;; Number slots
((member slot-name '(id loans))
#'<)
;; Symbol slots
((member slot-name '(category status))
#'(lambda (x y)
(string< (symbol-name x)
(symbol-name y)))))))
(sort objects #'(lambda (x y)
(funcall sort-fn
(slot-value x slot-name)
(slot-value y slot-name))))))
I then use it like this when generating a table of titles:
(defun all-titles ()
(let (all)
(doclass (x 'title)
(push x all))
all))
(dolist (x (my-sort (all-titles) 'name))
(html
(:tr ...)))
However, I cannot help thinking that there must be an easier way to
sort my objects on a certain slot. Is there?
I read in PCL about adding SQL-like features by modelling a table and
column class etc, but I found out about that long after most of my
implementation was done, so I don't want to back up now.
Thanks!
/Mathias
> (dolist (x (my-sort (all-titles) 'name))
> (html
> (:tr ...)))
This is a follow-up problem I got regarding the above. As this is a
web app I am developing I like to control the sorting through the URL,
so I send in a parameter called sort with the name of the column to
sort on:
http://localhost:8000/bibblan?list&sort=name
Then my problem is, how do I convert this string "name" (in this
example URL) to the symbol 'name? I made some investigation and there
are several ways, using INTERN, MAKE-SYMBOL, FIND-SYMBOL and READ-FROM-
STRING. And although I can EQ what comes out of these with 'name, they
cannot be used in my function, CLOS complains about "X not being a
slot" in my object. I am really confused and currently have had to
restort to something as twisted as this:
(let ((sym (cond ((string= sort "name") 'name)
((string= sort "author")
'author)
((string= sort "id") 'id)
((string= sort "language")
'language)
((string= sort "owner") 'owner)
((string= sort "genre") 'genre)
((string= sort "category")
'category)
((string= sort "location")
'location)
((string= sort "status")
'status)
((string= sort "loans") 'loans)
((string= sort "borrowed-by")
'borrowed-by)))
(sym2 (read-from-string sort)))
Above, `sym' works in a call to `my-sort', `sym2' does not:
(dolist (x (my-sort (all-titles) sym)) ;; Works
(html ...
(dolist (x (my-sort (all-titles) sym2)) ;; Does not work
(html ...
Can someone explain this?
/Mathias
PS. Also, at first I tried to use CASE to at least avoid some of the
craziness with COND, like this:
(case sort ("name" 'name)
...)
But that did now work. Maybe it does not work on strings...
From: Rainer Joswig
Subject: Re: Creating symbols from strings and their use as slot names (was: Getting a list of all objects of class X sorted on slot Y)
Date:
Message-ID: <joswig-0CC3EE.17164816052008@news-europe.giganews.com>
In article
<····································@34g2000hsf.googlegroups.com>,
Mathias Dahl <············@gmail.com> wrote:
> > (dolist (x (my-sort (all-titles) 'name))
> > � (html
> > � �(:tr ...)))
>
> This is a follow-up problem I got regarding the above. As this is a
> web app I am developing I like to control the sorting through the URL,
> so I send in a parameter called sort with the name of the column to
> sort on:
>
> http://localhost:8000/bibblan?list&sort=name
>
> Then my problem is, how do I convert this string "name" (in this
> example URL) to the symbol 'name? I made some investigation and there
> are several ways, using INTERN, MAKE-SYMBOL, FIND-SYMBOL and READ-FROM-
> STRING. And although I can EQ what comes out of these with 'name, they
> cannot be used in my function, CLOS complains about "X not being a
> slot" in my object. I am really confused and currently have had to
> restort to something as twisted as this:
>
SYMBOLS are uppercase.
(defclass foo ()
(bar))
CL-USER 10 > (let ((i (make-instance 'foo))
(bar-name "BAR"))
(setf (slot-value i (find-symbol bar-name)) 'baz)
(slot-value i 'bar))
BAZ
> (let ((sym (cond ((string= sort "name") 'name)
> ((string= sort "author")
> 'author)
> ((string= sort "id") 'id)
> ((string= sort "language")
> 'language)
> ((string= sort "owner") 'owner)
> ((string= sort "genre") 'genre)
> ((string= sort "category")
> 'category)
> ((string= sort "location")
> 'location)
> ((string= sort "status")
> 'status)
> ((string= sort "loans") 'loans)
> ((string= sort "borrowed-by")
> 'borrowed-by)))
> (sym2 (read-from-string sort)))
>
> Above, `sym' works in a call to `my-sort', `sym2' does not:
>
> (dolist (x (my-sort (all-titles) sym)) ;; Works
> (html ...
>
> (dolist (x (my-sort (all-titles) sym2)) ;; Does not work
> (html ...
>
> Can someone explain this?
>
> /Mathias
>
> PS. Also, at first I tried to use CASE to at least avoid some of the
> craziness with COND, like this:
>
> (case sort ("name" 'name)
> ...)
>
> But that did now work. Maybe it does not work on strings...
--
http://lispm.dyndns.org/
Mathias Dahl <············@gmail.com> writes:
>> (dolist (x (my-sort (all-titles) 'name))
>> � (html
>> � �(:tr ...)))
>
> This is a follow-up problem I got regarding the above. As this is a
> web app I am developing I like to control the sorting through the URL,
> so I send in a parameter called sort with the name of the column to
> sort on:
>
> http://localhost:8000/bibblan?list&sort=name
>
> Then my problem is, how do I convert this string "name" (in this
> example URL) to the symbol 'name?
> [...]
You are making several ellipsis when you write "the symbol 'name".
First 'name is not the external representation of a symbol, but of a
list of two elements: the symbol named COMMON:QUOTE, and some symbol
whose name bears some ressemblance to the string "name", found in some
package. It all depends on the setting of various global special
variables when that symbol is read.
Then if you don't do anything you are left with this list, which is
not a symbol. There are several ways to get a symbol from it. One
would be to apply CL:CAR on this list, so you'd get the symbol
CL:QUOTE. With CL:SECOND, you'd get that symbol read from the string
"name". Another way, is to evaluate this list. Since CL:QUOTE is a
special operator that prevents the evaluation of its argument and that
returns it unchanged, you'd get that symbol read from the string
"name".
But for us to tell what symbol it is, you'd have to tell us what is
the value of CL:*PACKAGE* and CL:*READTABLE* (and a few other special
variables).
Try to evaluate:
(let ((*read-base* 30))
(read-from-string "'name"))
(let ((*readtable* (copy-readtable nil)))
(setf (readtable-case *readtable*) :preserve)
(dolist (object (read-from-string "'name"))
(print `(,(symbol-package object) ,(symbol-name object)))))
So assuming you have the default values for the special variables, and
that you read and evaluate 'name, you would get as result of this
evaluation the symbol COMMON-LISP-USER:NAME, that is, a symbol named
"NAME", not "name".
> (sym2 (read-from-string sort)))
>
> Above, `sym' works in a call to `my-sort', `sym2' does not:
>
> (dolist (x (my-sort (all-titles) sym)) ;; Works
> (html ...
>
> (dolist (x (my-sort (all-titles) sym2)) ;; Does not work
> (html ...
>
> Can someone explain this?
Read better and more deeply CLHS about READ-FROM-STRING ;-)
That is, when you read a CLHS page, you must also follow the SEE ALSO
links. And then on.
In this case, the relevant path is READ-FROM-STRING --> READ --> *READTABLE*
and therefore, you will be also currious enough to read about READTABLE
and READTABLE-CASE.
> PS. Also, at first I tried to use CASE to at least avoid some of the
> craziness with COND, like this:
>
> (case sort ("name" 'name)
> ...)
>
> But that did now work. Maybe it does not work on strings...
Yes, CASE works perfect well with strings. But again, read more precisely CLHS.
What operator is used to compare the expression with the constants?
Try:
(let ((str #1="hello"))
(case str
("bonjour" :fr)
("buenos dias" :es)
(#1# :en)))
--> :EN ; notice the case!
Otherwise, you can trivially write a macro:
;; not tested:
(defmacro scase (string-expression &rest clauses)
(let ((val (gensym)))
`(let ((,val ,string-expression))
(cond
,@(mapcar (lambda (clause)
(when (atom clause) (error "Expected a clause ((string...) expr...), not ~S" clause))
(if (atom (first clause))
(if (eq (first clause) 'otherwise)
`(t ,@(rest clause))
`((string= ,(first clause) ,val) ,@(rest clause)))
`((member ,val ',(first clause) :test (function string=)) ,@(rest clause))))
clauses)))))
And if it's to slow, you can later implement it with perfect hashes or
decision nets or whatever you like instead of using COND.
--
__Pascal Bourguignon__
> > Then my problem is, how do I convert this string "name" (in this
> > example URL) to the symbol 'name?
> > [...]
>
> You are making several ellipsis when you write "the symbol 'name".
I am sure I do more than that... :)
> Try to evaluate:
>
> (let ((*read-base* 30))
> (read-from-string "'name"))
=> '630674, 5
> (let ((*readtable* (copy-readtable nil)))
> (setf (readtable-case *readtable*) :preserve)
> (dolist (object (read-from-string "'name"))
> (print `(,(symbol-package object) ,(symbol-name object)))))
(#<The COMMON-LISP package> "QUOTE")
(#<The BIBBLAN package> "name")
> So assuming you have the default values for the special variables, and
> that you read and evaluate 'name, you would get as result of this
> evaluation the symbol COMMON-LISP-USER:NAME, that is, a symbol named
> "NAME", not "name".
> Read better and more deeply CLHS about READ-FROM-STRING ;-)
> That is, when you read a CLHS page, you must also follow the SEE ALSO
> links. And then on.
> In this case, the relevant path is READ-FROM-STRING --> READ --> *READTABLE*
> and therefore, you will be also currious enough to read about READTABLE
> and READTABLE-CASE.
This is simply beyond me, currently. I think I understand a bit of
what you are trying to convey but I don't succeed fully.
From my code again:
(let ((sym (or (cond ((string= sort "name")
'name)
((string= sort "author")
'author)
...
((string= sort "borrowed-
by") 'borrowed-by))
'name)))
(dolist (x (get-sorted-titles (read-from-string
(string-upcase sort))))
(html
(:tr ...
I make the string uppercase before I try to read it. Still I get this
error when running it:
there is no index on slot AUTHOR
[Condition of type SIMPLE-ERROR]
What is the slot symbol *really* if I used the DEFCLASS posted
earlier?
> Yes, CASE works perfect well with strings. But again, read more precisely CLHS.
> What operator is used to compare the expression with the constants?
Are you trying to tell me this:
(eq "a" "a")
=> NIL
?
> (let ((str #1="hello"))
> (case str
> ("bonjour" :fr)
> ("buenos dias" :es)
> (#1# :en)))
> --> :EN ; notice the case!
Yes, isn't that because :en is printed as :EN? Like 'a prints as 'A?
It does not help me understand how to use string values in a case
statement however. You indicate it is possible, still you provide a
quite complicated macro below suggesting that even if it is possible
it is not what one would try to do. Pascal, you speak in riddles! :)
> (defmacro scase (string-expression &rest clauses)
> ...
Way over my head, sorry :(
> And if it's to slow, you can later implement it with perfect hashes or
> decision nets or whatever you like instead of using COND.
Uh, okay, I will trust you on that one :)
If I get back to my code once more:
(let ((sym (or (cond ((string= sort "name")
'name)
((string= sort "author")
'author)
((string= sort "id") 'id)
((string= sort "language")
'language)
((string= sort "owner")
'owner)
((string= sort "genre")
'genre)
((string= sort "category")
'category)
((string= sort "location")
'location)
((string= sort "status")
'status)
((string= sort "loans")
'loans)
((string= sort "borrowed-
by") 'borrowed-by))
'name)))
(dolist (x (get-sorted-titles sym))
(html
The above works well but it itches so much when I see it! My guess is
you have already explained at least three times why that is the
easiest way to do it, but my guess is that I am one of the few on this
list that do not understand it.
Let me rephrase myself:
is there a "neater" way of doing what I do above to "dynamically"
create a symbol used in a call to a function I have defined?
/Mathias - still confused, maybe more :)
Mathias Dahl <············@gmail.com> writes:
>> Try to evaluate:
>>
>> (let ((*read-base* 30))
>> � �(read-from-string "'name"))
>
> => '630674, 5
Interpretation of "name" depends on some global variables, such as *READ-BASE*.
>> (let ((*readtable* (copy-readtable nil)))
>> � �(setf (readtable-case *readtable*) :preserve)
>> � �(dolist (object (read-from-string "'name"))
>> � � � � (print `(,(symbol-package object) ,(symbol-name object)))))
>
> (#<The COMMON-LISP package> "QUOTE")
> (#<The BIBBLAN package> "name")
or *READTABLE*.
>> Read better and more deeply CLHS about READ-FROM-STRING ;-)
>> That is, when you read a CLHS page, you must also follow the SEE ALSO
>> links. �And then on. �
>> In this case, the relevant path is READ-FROM-STRING --> READ --> *READTABLE*
>> and therefore, you will be also currious enough to read about READTABLE
>> and READTABLE-CASE.
>
> This is simply beyond me, currently. I think I understand a bit of
> what you are trying to convey but I don't succeed fully.
In simple terms, with the default settings, when you read "abc", you
get the symbol named "ABC" in the package "COMMON-LISP-USER", that is,
the symbol: COMMON-LISP-USER::ABC
Also, with the default settings, when you print the symbol ABC, you
get "ABC" printed, and when you PRINT the symbol named "Abc", you get
"|Abc|" printed, and when you PRINC the symbol named "Abc", you get
"Abc" printed.
(princ (intern "ABC"))
(princ (intern "Abc"))
(princ (intern "abc"))
> From my code again:
> [...]
> (dolist (x (get-sorted-titles (read-from-string
> (string-upcase sort))))
This should be ok.
> I make the string uppercase before I try to read it. Still I get this
> error when running it:
>
> there is no index on slot AUTHOR
> [Condition of type SIMPLE-ERROR]
>
> What is the slot symbol *really* if I used the DEFCLASS posted
> earlier?
It found the slot AUTHOR. You get another error, probably signaled by
your own code. Is there an index on this slot? Note that indexes are
not a CL notion, it must be something you've programmed, or provided
by a library you're using.
>> Yes, CASE works perfect well with strings. �But again, read more precisely CLHS.
>> What operator is used to compare the expression with the constants?
>
> Are you trying to tell me this:
>
> (eq "a" "a")
>
> => NIL
>
> ?
Yes.
>> (let ((str #1="hello"))
>> � (case str
>> � �("bonjour" � � :fr)
>> � �("buenos dias" :es)
>> � �(#1# � � � � � :en)))
>
>> --> :EN ; notice the case!
>
> Yes, isn't that because :en is printed as :EN? Like 'a prints as 'A?
> It does not help me understand how to use string values in a case
> statement however. You indicate it is possible, still you provide a
> quite complicated macro below suggesting that even if it is possible
> it is not what one would try to do. Pascal, you speak in riddles! :)
Well, I showed one thing, and had you notice another in passing. Sorry
about that. The above case will work, that is "hello" will be matched
by CASE, because it is the very same object that is returned by the
expression STR, and that is in the third clause, thanks to the reader
trick #1=/#1#.
(let ((str "hello"))
(case str
("bonjour" :fr)
("buenos dias" :es)
("hello" :en)))
would return NIL, because indeed (not (eql "hello" "hello")
>> (defmacro scase (string-expression &rest clauses)
>> ...
>
> Way over my head, sorry :(
It is not that complicated.
Just check the CLHS for each operator you don't know.
Also, for macros it is useful to use MACROEXPAND to see what they expand to on some examples:
(macroexpand '(scase str (("abc" "def") 1) ("ghi" 2) (otherwise 3)))
--> (LET ((#1=#:G10508 STR))
(COND ((MEMBER #1# '("abc" "def") :TEST #'STRING=) 1)
((STRING= "ghi" #1#) 2)
(T 3))) ;
T
You understand LET, COND, MEMBER, STRING= right?
> The above works well but it itches so much when I see it! My guess is
> you have already explained at least three times why that is the
> easiest way to do it, but my guess is that I am one of the few on this
> list that do not understand it.
>
> Let me rephrase myself:
>
> is there a "neater" way of doing what I do above to "dynamically"
> create a symbol used in a call to a function I have defined?
Try: (symbol-name 'author)
Try to use string-equal instead of string=
or (string= sort "AUTHOR")
or (eql 'author (string-upcase "author"))
You can just write:
(dolist (x (my-sort (all-titles)
(or (find-symbol (STRING-UPCASE sort))
*default-sort-key*)))
...)
--
__Pascal Bourguignon__
P� Mon, 19 May 2008 10:43:05 +0200, skrev Pascal J. Bourguignon
<···@informatimago.com>:
> Mathias Dahl <············@gmail.com> writes:
>
>>> Try to evaluate:
>>>
>>> (let ((*read-base* 30))
>>> � �(read-from-string "'name"))
>>
>> => '630674, 5
>
> Interpretation of "name" depends on some global variables, such as
> *READ-BASE*.
So wrap a with-standard-io-syntax around it. This sets all variables to
their default values and is a good guard against 'suprises'.
--------------
John Thingstad
> You can just write:
>
> (dolist (x (my-sort (all-titles)
> (or (find-symbol(STRING-UPCASE sort))
> *default-sort-key*)))
> ...)
That works! :)
I am sure I tried FIND-SYMBOL in my earlier attempts to solve my
problem, but maybe then I had some other stuff in there that made it
fail.
Anyway, now I can happily replace this:
(let
...
(sym (or (cond ((string= sort "name") 'name)
((string= sort "category") 'category)
((string= sort "author") 'author)
((string= sort "id") 'id)
((string= sort "language") 'language)
((string= sort "owner") 'owner)
((string= sort "added") 'added)
((string= sort "genre") 'genre)
((string= sort "category") 'category)
((string= sort "location") 'location)
((string= sort "state") 'state)
((string= sort "loans") 'loans)
((string= sort "borrowed-by") 'borrowed-by))
'added))
with this:
(let
...
(sym (or (find-symbol (string-upcase sort))
'added)))
Many thanks!
/Mathias
Mathias Dahl <············@gmail.com> writes:
>> You can just write:
>>
>> (dolist (x (my-sort (all-titles)
>> (or (find-symbol(STRING-UPCASE sort))
>> *default-sort-key*)))
>> ...)
>
> That works! :)
>
> I am sure I tried FIND-SYMBOL in my earlier attempts to solve my
> problem, but maybe then I had some other stuff in there that made it
> fail.
Of course.
Knight and the Lisp machine
A novice was trying to fix a broken Lisp machine by turning the
power off and on. Knight, seeing what the student was doing,
spoke sternly: "You cannot fix a machine by just power-cycling it
with no understanding of what is going wrong." Knight turned the
machine off and on. The machine worked.
http://www.c2i.ntu.edu.sg/AI+CI/Humor/AI_Jokes/AIKoans-DHillis.html
--
__Pascal Bourguignon__ http://www.informatimago.com/
"I have challenged the entire quality assurance team to a Bat-Leth
contest. They will not concern us again."
From: Thomas A. Russ
Subject: Re: Creating symbols from strings and their use as slot names
Date:
Message-ID: <ymi4p8ucgnf.fsf@blackcat.isi.edu>
Mathias Dahl <············@gmail.com> writes:
> If I get back to my code once more:
>
> (let ((sym (or (cond ((string= sort "name")
> 'name)
> ((string= sort "author")
> 'author)
> ((string= sort "id") 'id)
> ((string= sort "language")
> 'language)
> ((string= sort "owner")
> 'owner)
> ((string= sort "genre")
> 'genre)
> ((string= sort "category")
> 'category)
> ((string= sort "location")
> 'location)
> ((string= sort "status")
> 'status)
> ((string= sort "loans")
> 'loans)
> ((string= sort "borrowed-
> by") 'borrowed-by))
> 'name)))
> (dolist (x (get-sorted-titles sym))
> (html
>
> The above works well but it itches so much when I see it! My guess is
> you have already explained at least three times why that is the
> easiest way to do it, but my guess is that I am one of the few on this
> list that do not understand it.
>
> Let me rephrase myself:
>
> is there a "neater" way of doing what I do above to "dynamically"
> create a symbol used in a call to a function I have defined?
You will want to use the INTERN function with the appropriate package as
its second argument. You will also presumably want to upper-case the
names as they come in. So you should be able to replace most of the
COND statement with
(intern (string-upcase sort) "WHATEVER-PACKAGE-YOUR-SYMBOLS-ARE-IN")
Of course, you do lose the defaulting to the NAME symbol if there are no
matches.
--
Thomas A. Russ, USC/Information Sciences Institute
From: Ken Tilton
Subject: Re: Getting a list of all objects of class X sorted on slot Y
Date:
Message-ID: <482dad87$0$15168$607ed4bc@cv.net>
Mathias Dahl wrote:
> Hi!
>
> I am doing a library web app (loan book, return book, add book to
> collection etc) for work and decided to try out AllegroServe and
> AllegroCache. Normally I would have used SBCL + Hunchentoot + CL-SQL
> but on Windows those does not seem to play very well.
>
> This is my first try using CLOS and also something like AllegroCache
> instead of a plain SQL database and lists so the first problem I
> encountered was sorting the titles:
>
> (defclass title ()
> ((id :initarg :id :reader id :index :any-
> unique)
> (name :initarg :name :reader name)
> (author :initarg :author :reader author)
> (language :initarg :language :reader language)
> (owner :initarg :owner :reader owner)
> (genre :initarg :genre :reader genre)
> (category :initarg :category :reader category :index :any)
> (status :initarg :status :reader status :index :any)
> (loans :initarg :loans :reader loans :index :any)
> (borrowed-by :initarg :borrowed-by :reader borrowed-
> by :index :any))
> (:metaclass persistent-class))
>
> I came up with this, which works:
>
> (defun my-sort (objects slot-name)
> (let ((sort-fn
> (cond (;; String slots
> (member slot-name '(name author language owner genre
> borrowed-by))
> #'string<)
> ;; Number slots
> ((member slot-name '(id loans))
> #'<)
> ;; Symbol slots
> ((member slot-name '(category status))
> #'(lambda (x y)
> (string< (symbol-name x)
> (symbol-name y)))))))
> (sort objects #'(lambda (x y)
> (funcall sort-fn
> (slot-value x slot-name)
> (slot-value y slot-name))))))
>
> I then use it like this when generating a table of titles:
>
> (defun all-titles ()
> (let (all)
> (doclass (x 'title)
> (push x all))
> all))
>
> (dolist (x (my-sort (all-titles) 'name))
> (html
> (:tr ...)))
>
> However, I cannot help thinking that there must be an easier way to
> sort my objects on a certain slot.
Gosh, what you have does not look so bad. I mean, boom, yer done, you'll
never have to visit this code again. That said...
> Is there?
Yes. Let your DB do the sorting. Use an index cursor. Page 28 or so of
the 2.1.5 Acache manual I just found on-line.
ACache also has maps, map cursors, sets -- all sorts of things you can
use in clever ways to get stuff done.
You might also look at AGraph, but that might be more horsepower than
you need, but as you can see it is my latest crush (first few minutes):
http://video.google.com/videoplay?docid=-9173722505157942928
Final tip: Franz is great on support and even a non-bug question like
this is handled graciously.
cheers, kenny
--
http://smuglispweeny.blogspot.com/
http://www.theoryyalgebra.com/
ECLM rant:
http://video.google.com/videoplay?docid=-1331906677993764413&hl=en
ECLM talk:
http://video.google.com/videoplay?docid=-9173722505157942928&q=&hl=en
> Gosh, what you have does not look so bad. I mean, boom, yer done, you'll
> never have to visit this code again.
Well, it felt so ugly...
> > Is there?
>
> Yes. Let your DB do the sorting. Use an index cursor. Page 28 or so of
> the 2.1.5 Acache manual I just found on-line.
I noticed it in the manual too but for some reason did not bother to
try it out. Maybe it was because to use this the slot had to be
indexed and not all of mine were defined as such. But now they
are... :) Here is my less ugly take on my sorting problem:
(defun get-sorted-titles (sort-slot)
(let ((cur (create-index-cursor 'title sort-slot))
obj titles)
(loop
(setf obj (next-index-cursor cur))
(if (null obj)
(progn
(free-index-cursor cur)
(return))
(push obj titles)))
(reverse titles)))
I use it like this:
(setf *muu* (get-sorted-titles 'name))
and even more importantly I no longer seem to get problems making
symbols out of strings:
(setf *muu* (get-sorted-titles (read-from-string "name")))
However, I haven't injected that in my real code yet, maybe there is
some detail in there pertaining to Pascal's explanation (that I have
not grokked yet) that will make it fail, we'll see. You will be the
first ones to know, promise :)
> ACache also has maps, map cursors, sets -- all sorts of things you can
> use in clever ways to get stuff done.
Yes, sets, for example looks interesting although I haven't seen a use
for them in my little app yet.
> Final tip: Franz is great on support and even a non-bug question like
> this is handled graciously.
I will keep that in mind, thanks!
/Mathias - doing something real...
From: Ken Tilton
Subject: Re: Getting a list of all objects of class X sorted on slot Y
Date:
Message-ID: <482dc826$0$15206$607ed4bc@cv.net>
Mathias Dahl wrote:
>>Gosh, what you have does not look so bad. I mean, boom, yer done, you'll
>>never have to visit this code again.
>
>
> Well, it felt so ugly...
You were handrolling typed dispatch on <. How do we do typed dispatch in
Lisp? typecase or a GF. Mebbe a GF called generic< would have seemed
less ugly.
>
>
>>>Is there?
>>
>>Yes. Let your DB do the sorting. Use an index cursor. Page 28 or so of
>>the 2.1.5 Acache manual I just found on-line.
>
>
> I noticed it in the manual too but for some reason did not bother to
> try it out.
The concept is known as "readiness". You did not need it when you read
it (and you were at the same time overwhelmed by too much new
information all at once). It happens.
btw, this is one good reason to flip thru any interesting tool's manual
a few weeks after getting started to find the things that might make
sense now.
> Maybe it was because to use this the slot had to be
> indexed and not all of mine were defined as such.
There's another good reason. :)
> But now they
> are... :)
Good move. You do not have a huge volume app, but where a lot of data is
going to be processed you do not want to drag things into Lisp existence
more than necessary, and the AC tools have the best shot of optimizing
that (tho last I heard on some issue they had not thought yet to Lispify
slot-wise, so all slots got Lispified when the instance was. But by
using the tools you are poised to benefit from that optimization if they
decide to go that way.
> Here is my less ugly take on my sorting problem:
>
> (defun get-sorted-titles (sort-slot)
> (let ((cur (create-index-cursor 'title sort-slot))
> obj titles)
> (loop
> (setf obj (next-index-cursor cur))
> (if (null obj)
> (progn
> (free-index-cursor cur)
> (return))
> (push obj titles)))
> (reverse titles)))
Ah, the library pays by the line of code? <kidding!>:
(loop with cur = (create-index-cursor 'title sort-slot)
for obj = (next-index-cursor)
while obj collect it ;; i think that works
finally (free-index-cursor cur))
Untested.
>
> I use it like this:
>
> (setf *muu* (get-sorted-titles 'name))
>
> and even more importantly I no longer seem to get problems making
> symbols out of strings:
>
> (setf *muu* (get-sorted-titles (read-from-string "name")))
I saw some suggest find-symbol. Me, I have always used intern or
read-from-string, but in this case where you really are trying to get to
a symbol that should be defined, I like find-symbol.
> /Mathias - doing something real...
<g>
kenny
--
http://smuglispweeny.blogspot.com/
http://www.theoryyalgebra.com/
ECLM rant:
http://video.google.com/videoplay?docid=-1331906677993764413&hl=en
ECLM talk:
http://video.google.com/videoplay?docid=-9173722505157942928&q=&hl=en
Ken Tilton <···········@optonline.net> writes:
> Mathias Dahl wrote:
>> I use it like this:
>> (setf *muu* (get-sorted-titles 'name))
>> and even more importantly I no longer seem to get problems making
>> symbols out of strings:
>> (setf *muu* (get-sorted-titles (read-from-string "name")))
>
> I saw some suggest find-symbol. Me, I have always used intern or
> read-from-string, but in this case where you really are trying to get
> to a symbol that should be defined, I like find-symbol.
Well, FIND-SYMBOL is for when you don't want to intern the symbol if
it is not already there.
In this case it is indicated because we don't want to intern random
symbols coming from the web clients. They would be useless memory
leaks.
--
__Pascal Bourguignon__
From: Thomas A. Russ
Subject: Re: Getting a list of all objects of class X sorted on slot Y
Date:
Message-ID: <ymi4p8ydpu8.fsf@blackcat.isi.edu>
Mathias Dahl <············@gmail.com> writes:
> Hi!
>
> I am doing a library web app (loan book, return book, add book to
> collection etc) for work and decided to try out AllegroServe and
> AllegroCache. Normally I would have used SBCL + Hunchentoot + CL-SQL
> but on Windows those does not seem to play very well.
>
> This is my first try using CLOS and also something like AllegroCache
> instead of a plain SQL database and lists so the first problem I
> encountered was sorting the titles:
>
> (defclass title ()
> ((id :initarg :id :reader id :index :any-
> unique)
> (name :initarg :name :reader name)
> (author :initarg :author :reader author)
> (language :initarg :language :reader language)
> (owner :initarg :owner :reader owner)
> (genre :initarg :genre :reader genre)
> (category :initarg :category :reader category :index :any)
> (status :initarg :status :reader status :index :any)
> (loans :initarg :loans :reader loans :index :any)
> (borrowed-by :initarg :borrowed-by :reader borrowed-
> by :index :any))
> (:metaclass persistent-class))
>
> I came up with this, which works:
>
> (defun my-sort (objects slot-name)
> (let ((sort-fn
> (cond (;; String slots
> (member slot-name '(name author language owner genre
> borrowed-by))
> #'string<)
> ;; Number slots
> ((member slot-name '(id loans))
> #'<)
> ;; Symbol slots
> ((member slot-name '(category status))
> #'(lambda (x y)
> (string< (symbol-name x)
> (symbol-name y)))))))
This can actually be simplified somewhat by taking advantage of a few
features of Lisp/CLOS. First of all, symbols are also "string
designators", so you can call string comparison functions like STRING<
on symbols and they will be coerced to strings using the STRING
function, which for symbols just does SYMBOL-NAME.
Secondly, if the slot reader uses the same name as the slot, you can
exploit the fact that there are generic functions created for slot
access. That can simplify your sort function a fair bit:
(defun get-slot-comparison-function (slot-name)
;; Assuming slots are either numeric or string/symbol valued.
(if (member slot-name '(id loans))
#'<
#'string<))
(defun my-sort (objects slot-name)
(sort objects (get-slot-comparison-function slot-name)
:key slot-name))
--
Thomas A. Russ, USC/Information Sciences Institute
Mathias Dahl <············@gmail.com> writes:
> Hi!
>
> I am doing a library web app (loan book, return book, add book to
> collection etc) for work and decided to try out AllegroServe and
> AllegroCache. Normally I would have used SBCL + Hunchentoot + CL-SQL
> but on Windows those does not seem to play very well.
>
> This is my first try using CLOS and also something like AllegroCache
> instead of a plain SQL database and lists so the first problem I
> encountered was sorting the titles:
>
> (defclass title ()
> ((id :initarg :id :reader id :index :any-
> unique)
> (name :initarg :name :reader name)
> (author :initarg :author :reader author)
> (language :initarg :language :reader language)
> (owner :initarg :owner :reader owner)
> (genre :initarg :genre :reader genre)
> (category :initarg :category :reader category :index :any)
> (status :initarg :status :reader status :index :any)
> (loans :initarg :loans :reader loans :index :any)
> (borrowed-by :initarg :borrowed-by :reader borrowed-
> by :index :any))
> (:metaclass persistent-class))
>
> I came up with this, which works:
>
> (defun my-sort (objects slot-name)
> (let ((sort-fn
> (cond (;; String slots
> (member slot-name '(name author language owner genre
> borrowed-by))
> #'string<)
> ;; Number slots
> ((member slot-name '(id loans))
> #'<)
> ;; Symbol slots
> ((member slot-name '(category status))
> #'(lambda (x y)
> (string< (symbol-name x)
> (symbol-name y)))))))
> (sort objects #'(lambda (x y)
> (funcall sort-fn
> (slot-value x slot-name)
> (slot-value y slot-name))))))
>
> I then use it like this when generating a table of titles:
>
> (defun all-titles ()
> (let (all)
> (doclass (x 'title)
> (push x all))
> all))
>
> (dolist (x (my-sort (all-titles) 'name))
> (html
> (:tr ...)))
>
> However, I cannot help thinking that there must be an easier way to
> sort my objects on a certain slot. Is there?
Note that SORT takes also a :KEY parameter to extract the key on which
the items are to be sorted. So you could also write:
(defgeneric lessp (a b)
(:method ((a string) (b string)) (string< a b))
(:method ((a symbol) (b symbol)) (string< (string a) (string b)))
(:method ((a real) (b real)) (< a b))
(:method ((a t) (b t))
(error "The types of the arguments to LESSP are not compatible: ~S vs. ~S"
(type-of a) (type-of b))))
(defun my-sort (books field)
(sort books (function lessp) :key (lambda (x) (slot-value x field))))
Or:
(defgeneric make-lessp (x)
(:method ((x string)) (declare (ignore x)) (function string<))
(:method ((x symbol)) (declare (ignore x)) (lambda (a b) (string< (string a) (string b))))
(:method ((x real)) (declare (ignore x)) (function <))
(:method ((x t))
(error "MAKE-LESSP doesn't know the order of values of type ~S"
(type-of x))))
(defun my-sort (books field)
(sort books (make-lessp (slot-value (first books) field))
:key (lambda (x) (slot-value x field))))
--
__Pascal Bourguignon__ http://www.informatimago.com/
PLEASE NOTE: Some quantum physics theories suggest that when the
consumer is not directly observing this product, it may cease to
exist or will exist only in a vague and undetermined state.
P� Sun, 08 Jun 2008 12:49:46 +0200, skrev Pascal J. Bourguignon
<···@informatimago.com>:
>
> (defgeneric lessp (a b)
> (:method ((a string) (b string)) (string< a b))
> (:method ((a symbol) (b symbol)) (string< (string a) (string b)))
> (:method ((a real) (b real)) (< a b))
> (:method ((a t) (b t))
> (error "The types of the arguments to LESSP are not compatible: ~S
> vs. ~S"
> (type-of a) (type-of b))))
> (defun my-sort (books field)
> (sort books (function lessp) :key (lambda (x) (slot-value x field))))
>
>
Nice one!
Note that string< will automatically convert a symbol to a string so the
(string a) is unneccesary.
(A gotha is nil which gets compared as "NIL".)
--------------
John Thingstad