From: Mark Carter
Subject: Extracting key-values from a list
Date: 
Message-ID: <426e7095$0$79453$14726298@news.sunsite.dk>
There was a link to a book on this group some time ago, giving some 
practical eamples of how to use lisp. One example was on how to build up 
a database of your CD collection, or something.

In it, they gave a method for finding the value in a list whose elements 
were stored in key - value order. The problem is, I can't remember the 
name of the book, or the function. Any pointers?

So, for example, I've got a list lst that looks like this:
(TIME (5 51 14 23 4 2005 5 T 0) REMOTE "100.10.10.10" WEBSITENAME "dfgv" 
DESCRIPTION "describes the website" URL NIL)

Suppose I want to be able to extract the description. I want to do 
something like
(wtf-is-it-called lst 'DESCRIPTION)
and have it return the value
"describes the website"

What function should I be using instead of wtf-is-it-called?
Sorry for such a n00b question.

From: Chris Riesbeck
Subject: Re: Extracting key-values from a list
Date: 
Message-ID: <criesbeck-6F993E.12044926042005@individual.net>
In article <·························@news.sunsite.dk>,
 Mark Carter <···········@yahoo.co.uk> wrote:

> There was a link to a book on this group some time ago, giving some 
> practical eamples of how to use lisp. One example was on how to build up 
> a database of your CD collection, or something.

Possibly Seibel's Practical Common Lisp
> 
> In it, they gave a method for finding the value in a list whose elements 
> were stored in key - value order. The problem is, I can't remember the 
> name of the book, or the function. Any pointers?
> 
> So, for example, I've got a list lst that looks like this:
> (TIME (5 51 14 23 4 2005 5 T 0) REMOTE "100.10.10.10" WEBSITENAME "dfgv" 
> DESCRIPTION "describes the website" URL NIL)
> 
> Suppose I want to be able to extract the description. I want to do 
> something like
> (wtf-is-it-called lst 'DESCRIPTION)
> and have it return the value
> "describes the website"
> 
> What function should I be using instead of wtf-is-it-called?

getf
From: Mark Carter
Subject: Re: Extracting key-values from a list
Date: 
Message-ID: <426e96e9$0$79456$14726298@news.sunsite.dk>
Chris Riesbeck wrote:
>  Mark Carter <···········@yahoo.co.uk> wrote:
>>What function should I be using instead of wtf-is-it-called?
> getf

Many thanks. That hit the spot!

No doubt it's obvious that I'm new to Lisp. I've decided to write a 
small webapp in CLISP on Debian. I decided to do without databases, so 
what happens is that a user can log new records. Another process comes 
along and does some aggregation on them. The beauty of Lisp is that it 
it's all done as sexprs, so I don't have any parsing problems. It's all 
just Lisp.
From: Sam Steingold
Subject: Re: Extracting key-values from a list
Date: 
Message-ID: <u8y35cnas.fsf@gnu.org>
> * Mark Carter <···········@lnubb.pb.hx> [2005-04-26 20:30:50 +0100]:
>
> No doubt it's obvious that I'm new to Lisp. I've decided to write a
> small webapp in CLISP on Debian. I decided to do without databases, so
> what happens is that a user can log new records. Another process comes
> along and does some aggregation on them. The beauty of Lisp is that it
> it's all done as sexprs, so I don't have any parsing problems. It's
> all just Lisp.

note that you can use alists (association lists)
<http://www.lisp.org/HyperSpec/Body/fun_assoccm_a_assoc-if-not.html>
or hash tables <http://www.lisp.org/HyperSpec/Body/acc_gethash.html>
instead of plists (property lists)
<http://www.lisp.org/HyperSpec/Body/acc_getf.html>

(both hash tables and association lists are printed as SEXPs in CLISP).

Good luck!

-- 
Sam Steingold (http://www.podval.org/~sds) running w2k
<http://pmw.org.il/> <http://www.openvotingconsortium.org/>
<http://ffii.org/> <http://www.honestreporting.com>
Why do we want intelligent terminals when there are so many stupid users?
From: Mark Carter
Subject: Re: Extracting key-values from a list
Date: 
Message-ID: <426eacaf$0$79462$14726298@news.sunsite.dk>
Sam Steingold wrote:
>>* Mark Carter <···········@lnubb.pb.hx> [2005-04-26 20:30:50 +0100]:
>>
>>No doubt it's obvious that I'm new to Lisp. I've decided to write a
>>small webapp in CLISP on Debian. I decided to do without databases, so
>>what happens is that a user can log new records. Another process comes
>>along and does some aggregation on them. The beauty of Lisp is that it
>>it's all done as sexprs, so I don't have any parsing problems. It's
>>all just Lisp.
> 
> 
> note that you can use alists (association lists)
> <http://www.lisp.org/HyperSpec/Body/fun_assoccm_a_assoc-if-not.html>
> or hash tables <http://www.lisp.org/HyperSpec/Body/acc_gethash.html>
> instead of plists (property lists)
> <http://www.lisp.org/HyperSpec/Body/acc_getf.html>
> 
> (both hash tables and association lists are printed as SEXPs in CLISP).
> 
> Good luck!

Many thanks.

I just had a look at Paul Graham's Lisp in Web-Based Applications:
http://lib.store.yahoo.com/lib/paulgraham/bbnexcerpts.txt

Like Graham, I am taking an incremental approach to building a web app. 
Unlike Graham, I'm not currently using macros, or expect to become a 
millionaire from it :( .

Actually, I find that I need to take an incremental approach anyway, 
because I am quite unfamiliar with Lisp. So everything is a bit more 
ponderous for me. I note, however, that by using my approach of logged 
lists, rather than say MySql, I can be much more experimental, and 
expand and change my design as I become more familiar with the 
problem-space.

OTOH, maybe one man's bottom-up design methodology is another man's lack 
of foresight. ;)
From: Pascal Bourguignon
Subject: Re: Extracting key-values from a list
Date: 
Message-ID: <87hdhtp7ib.fsf@thalassa.informatimago.com>
Mark Carter <···········@yahoo.co.uk> writes:

> Chris Riesbeck wrote:
>>  Mark Carter <···········@yahoo.co.uk> wrote:
>>>What function should I be using instead of wtf-is-it-called?
>> getf
>
> Many thanks. That hit the spot!
>
> No doubt it's obvious that I'm new to Lisp. I've decided to write a
> small webapp in CLISP on Debian. I decided to do without databases, so
> what happens is that a user can log new records. Another process comes
> along and does some aggregation on them. The beauty of Lisp is that it
> it's all done as sexprs, so I don't have any parsing problems. It's
> all just Lisp.

However, it's really easy to use a sql database from lisp.
For example, Postgresql in clisp.

Here is a little wrapper that allow me to hide the SQL:


(asdf:operate 'asdf:load-op :pg)
(use-package "POSTGRESQL")


(defclass db-object ()
  ((id :accessor db-ident :initarg :id :initform nil))
  (:documentation "Objects in the database are identified by the ID column."))

(defmethod atom-to-pg ((self db-object)) 
  (format nil "~A"   (db-ident self)))



(defclass date ()
  ((year  :reader date-year  :initarg :year  
          :initform 2005     :type (integer 300))
   (month :reader date-month :initarg :month
          :initform 1        :type (integer 1 12))
   (day   :reader date-day   :initarg :day
          :initform 1        :type (integer 1 31))))

(defmethod atom-to-pg ((self date))      
  (format nil "'~A'" (date-iso8601 self)))

(defun date (y m d)
  (make-instance 'date :year y :month m :day d))



(defclass db () 
  ((connection :accessor db-connection :initarg :connection)
   (columns    :initform (make-hash-table))))
  

(defvar *db* nil)

(defmethod atom-to-pg ((self (eql nil)))
  "NULL")

(defmethod atom-to-pg ((self symbol))
  (string-downcase self))

(defmethod atom-to-pg ((self real))
  (format nil "~A" self))

(defmethod atom-to-pg ((self string))
  (format nil "'~A'" (string-replace self "'" "''" t t)))


(defun where-sexp-to-pg (sexp)
  "Converts a sexp into a SQL WHERE clause."
  (if (atom sexp)
    (atom-to-pg sexp)
    (case (car sexp)
      ((= < <= > >=)
       (if (= 2 (length (cdr sexp)))
         (format nil "(~A ~A ~A)"
                 (where-sexp-to-pg (first (cdr sexp)))
                 (car sexp)
                 (where-sexp-to-pg (second (cdr sexp))))
         (error "Not implemented yet.")))
      ((/=)
       (if (= 2 (length (cdr sexp)))
         (format nil "(~A != ~A)"
                 (where-sexp-to-pg (first (cdr sexp)))
                 (where-sexp-to-pg (second (cdr sexp))))
         (error "Not implemented yet.")))
      ((and or * + - /)
       (format nil "(~{~A~^ ~})"
               (cdr (mapcan
                     (lambda (item) (list (car sexp) (where-sexp-to-pg item)))
                     (cdr sexp)))))
      ((not min max)
       (when (/= 1 (length (cdr sexp)))
         (error "~A takes one argument, not ~S." (car sexp) (cdr sexp)))
       (format nil "~A(~A)"  (car sexp) (where-sexp-to-pg (second sexp))))
      ((select)
       (format nil "(~{~A~^ ~})"
               (mapcar (lambda (item) (if (consp item) (where-sexp-to-pg item) item))
                       sexp)))
      (t (error "Invalid expression ~S" sexp)))))


(defmethod db-columns ((self db) table)
  (let ((columns (gethash table (slot-value *db* 'columns))))
    (unless columns
      (setf columns (mapcar 
                     (lambda (name) (intern (string-upcase name) :keyword))
                     (pg-columns (db-connection *db*) (string-downcase table)))
            (gethash table (slot-value *db* 'columns)) columns))
    columns))
  

(defmethod db-select ((self db) table &key where order)
  (let ((results '())
        (columns (db-columns *db* table)))
    (pg-for-each
     (db-connection *db*)
     (format nil
       "SELECT * FROM ~A ~:[~;~:*WHERE ~A~] ~:[~;~:*ORDER BY ~{~A~^ ~}~]"
       table (and where (where-sexp-to-pg where)) 
       (and order (if (atom order) (list order) order)))
     (lambda (row)
       (push (apply (function make-instance) table
                    (mapcan (function list) columns row)) results)))
    results))


(defmethod db-delete ((self db) table where)
  (pg-exec (db-connection *db*)
           (format nil "DELETE FROM ~A WHERE ~A" 
                   table (where-sexp-to-pg where))))


(defmethod db-insert ((self db) table item)
  (pg-exec
   (db-connection *db*)
   (format nil "INSERT INTO ~A (~{~A~^, ~}) VALUES (~{~A~^, ~})" 
           table
           (remove :id (db-columns *db* table))
           (mapcar
            (lambda (column)
              (atom-to-pg
               (funcall (intern (format nil "~A-~A" table column)) item)))
            (remove :id (db-columns *db* table))))))


(defmethod db-update ((self db) table item)
  (pg-exec
   (db-connection *db*)
   (format nil "UPDATE ~A SET ~{~A=~A~^,~} WHERE ~A" 
           table
           (mapcan (function list)
                   (remove :id (db-columns *db* table))
                   (mapcar (lambda (column)
                             (atom-to-pg
                              (funcall
                               (intern (format nil "~A-~A"  table column))
                               item)))
                    (remove :id (db-columns *db* table))))
           (where-sexp-to-pg `(= id ,item)))))


(defmethod db-map ((self db) table fun &key where order)
  (dolist (item (db-select self table :where where :order order))
    (funcall fun item)))

;; ------------------------------
;; which allows me to write:


(with-pg-connection (pg "database" "user")
 (let ((*db* (make-instance 'db :connection pg)))

  (let ((date (some-date)) (title (some-title)) (text (some-text)))
    (let ((com (first (db-select *db* 'commentary
                              :where `(= date (select (min date) 
                                                 from commentary
                                                 where (<= ,date date)))))))
    (if com
      (progn
        (setf (commentary-date       com) date
              (commentary-title      com) title
              (commentary-text       com) text)
        (db-update *db* 'commentary com))
      (progn
        (setf com (make-instance 'commentary
                    :date  date
                    :title title
                    :text  text))
        (db-insert *db* 'commentary com)))
     ))))


;; and which is not much different from what you'd write with hashtables,
;; (actually with hash tables you have only one key, so a "select" would
;; involve several hash tables (indexes) or you'd have to scan with maphash).
   
  (let ((date (some-date)) (title (some-title)) (text (some-text)))
   (let ((com (gethash (reduce (function min)
                               (maphash (lambda (k v) 
                                            (if (<= date v)
                                               v +end-of-the-universe-date+))
                                        *commentary-date-map*))
                        *commentary-date-map*)))
    (if com
      (progn
        ;; updating date; date may change:
        (setf (gethash (commentary-date com) *commentary-date-map*) nil)
        (setf (commentary-date       com) date
              (commentary-title      com) title
              (commentary-text       com) text)
        (setf (gethash date *commentary-date-map*) com))
      (progn
        (setf com (make-instance 'commentary
                    :date  date
                    :title title
                    :text  text))
        ;; insertion.
        (setf (gethash date *commentary-date-map*) com))
     )))


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
You never feed me.
Perhaps I'll sleep on your face.
That will sure show you.
From: ······@nomail.com
Subject: Re: Extracting key-values from a list
Date: 
Message-ID: <m06t61pgjm8pe1sogjbq16t00up0gotht4@4ax.com>
On Tue, 26 Apr 2005 17:47:27 +0100, Mark Carter
<···········@yahoo.co.uk> wrote:

>What function should I be using instead of wtf-is-it-called?

That list is a plist.  The standard Common Lisp function to get an
item from it is "getf".  E.g. (getf list 'description)
From: Paolo Amoroso
Subject: Re: Extracting key-values from a list
Date: 
Message-ID: <87ll75wfvy.fsf@plato.moon.paoloamoroso.it>
Mark Carter <···········@yahoo.co.uk> writes:

> There was a link to a book on this group some time ago, giving some
> practical eamples of how to use lisp. One example was on how to build
> up a database of your CD collection, or something.

Practical Common Lisp
http://www.gigamonkeys.com/book


Paolo
-- 
Why Lisp? http://lisp.tech.coop/RtL%20Highlight%20Film
Recommended Common Lisp libraries/tools (see also http://clrfi.alu.org):
- ASDF/ASDF-INSTALL: system building/installation
- CL-PPCRE: regular expressions
- UFFI: Foreign Function Interface