From: ·····@yagc.ndo.co.uk
Subject: Bewildered Lisp newbie asks..is there a "right" way to code Lisp?
Date: 
Message-ID: <87eknusdaz.fsf@yagc.ndo.co.uk>
..actually I'm a very oldbie. I learnt the basics of LISP back in my CS
undergrad course about nine (egad!) years ago. Recently I've developed
the habit of learning a new language every year, to keep me
flexible. Last year it was Python. The year before it was Java.  My
day job is mostly C/C++ graphics development.  This year it's LISP's
turn. It's quite obvious that it's going to be a bit more demanding to
learn than the other two..

Anyway, one of my first exercises is to write a small extendable
interpreter in the style of Pythons' CMD : see -
http://docs.python.org/lib/module-cmd.html - and immediately hit a
wall. Unlike most other langages (apart from C++) the problem wasn't
*how* to do it, but *which* way to do it. For the purposes of this
post I'll narrow my bewilderment to simple function despatch.

The First Way.  
============= 

( the way I'd have done it in CS class ).

; If the user types 'help one' they get this
(defun help-message-one () "This is help message one")

; If the user types 'help two' they get this
(defun help-message-two () "This is help message two")

; if the user types 'help hello' they get this
(defun hello-world () "Hello world")

; create a list matching help keywords and functions
(defparameter helpers '((one help-message-one) 
                        (two help-message-two) 
                        (hello hello-world)))

; given a list such as the one above an a keyword, isolate the
; function name and call it
(defun call-fun-if-needed (name elem)
	   (if (eql name (car elem))
	       (funcall (cadr elem))
	       nil))

; a sligtly more convieneint function to use our global list of helper
; functions

(defun call-help (name) 
       (mapcar #'(lambda (elem) 
               (call-fun-if-needed name elem)) helpers))
 
So far, so good thinks, I. But is mapcar the right way to do this? I
could just as easily use a loop .. do (or dolist) instead of mapcar in
call-fun-if-needed, so:

The Second Way.
==============

(defun call-fun-if-needed (name elem)
	   (mapcar #'funcall 
	    (loop for x in elem
		 when (eql name (car x))
		 collect (cadr x))))

This time I'm using loop to iterate my helper list and mapcar to
actually invoke the function, although a straight (funcall (car form))
would do.. so, is iterating with loop "better" than mapcar? And what's
the reason to use dolist instead of loop?

Then, yet a third way occured to me, assoc is made just for this kind
of thing..

The Third way.
=============

(defparameter helpers '((one . help-message-one) 
                        (two . help-message-two) 
                        (hello . hello-world)))

(defun call-helper (name helper-list)
	   (let ((helper (cdr (assoc name helper-list))))
	     (if helper
		 (funcall helper))))


CL-USER> (call-helper 'hello helpers)
"Hello world"

So, which is the "right" way? Is it just a matter of taste, or are
there implications about memory usage and optimisation according to
which way you do it?

As an aside, I'm aware that this kind of thing can be compiled into
the language with macros, and am damn impressed..but hey, I'm an old
newbie, one thing at a time..etc..

Am I also correct in assuming that SBCL and SLIME is a good
environment for a newbie (albiet a newbie habitual VI user..)? I've
looked at Jabberwocky and although it's very nice and useful with the
integrated debugger, it has the usual problem of being a bit on the
slow side. My P600 just isn't up to it..would CMUCL be a better choice
(on stability grounds)?

John Connors
http://badbyteblues.blogspot.com

From: Kenny Tilton
Subject: Re: Bewildered Lisp newbie asks..is there a "right" way to code Lisp?
Date: 
Message-ID: <bFeFc.15469$4h7.1784595@twister.nyc.rr.com>
·····@yagc.ndo.co.uk wrote:

  Recently I've developed
> the habit of learning a new language every year, to keep me
> flexible. Last year it was Python. The year before it was Java.  My
> day job is mostly C/C++ graphics development.

OpenGL? I could use some help on the Cello project when I get back to that.

> Anyway, one of my first exercises is to write a small extendable
> interpreter in the style of Pythons' CMD : see -
> http://docs.python.org/lib/module-cmd.html - and immediately hit a
> wall. Unlike most other langages (apart from C++) the problem wasn't
> *how* to do it, but *which* way to do it. For the purposes of this
> post I'll narrow my bewilderment to simple function despatch.
> 
> The First Way.  
> ============= 
> 
> ( the way I'd have done it in CS class ).
> 
> ; If the user types 'help one' they get this
> (defun help-message-one () "This is help message one")
> 
> ; If the user types 'help two' they get this
> (defun help-message-two () "This is help message two")
> 
> ; if the user types 'help hello' they get this
> (defun hello-world () "Hello world")
> 
> ; create a list matching help keywords and functions
> (defparameter helpers '((one help-message-one) 
>                         (two help-message-two) 
>                         (hello hello-world)))
> 
> ; given a list such as the one above an a keyword, isolate the
> ; function name and call it
> (defun call-fun-if-needed (name elem)
> 	   (if (eql name (car elem))
> 	       (funcall (cadr elem))
> 	       nil))

nit: You can just: (when (eql...) (funcall...))

> 
> ; a sligtly more convieneint function to use our global list of helper
> ; functions
> 
> (defun call-help (name) 
>        (mapcar #'(lambda (elem) 
>                (call-fun-if-needed name elem)) helpers))

will there ever be more than one help function for the same name? if 
not, this code continues iterating needlessly after the match is found. 
not the end of the world in this case, just a Bad Sign for the approach.

>  
> So far, so good thinks, I. But is mapcar the right way to do this? I
> could just as easily use a loop .. do (or dolist) instead of mapcar in
> call-fun-if-needed, so:
> 
> The Second Way.
> ==============
> 
> (defun call-fun-if-needed (name elem)
> 	   (mapcar #'funcall 
> 	    (loop for x in elem
> 		 when (eql name (car x))
> 		 collect (cadr x))))
> 
> This time I'm using loop to iterate my helper list and mapcar to
> actually invoke the function, although a straight (funcall (car form))
> would do.. so, is iterating with loop "better" than mapcar? And what's
> the reason to use dolist instead of loop?

Indirect answer: It took me a long time to get into loop, because of its 
unusual syntax. and it is just syntax.

> 
> Then, yet a third way occured to me, assoc is made just for this kind
> of thing..
> 
> The Third way.
> =============
> 
> (defparameter helpers '((one . help-message-one) 
>                         (two . help-message-two) 
>                         (hello . hello-world)))
> 
> (defun call-helper (name helper-list)
> 	   (let ((helper (cdr (assoc name helper-list))))
> 	     (if helper
> 		 (funcall helper))))
> 
> 
> CL-USER> (call-helper 'hello helpers)
> "Hello world"
> 
> So, which is the "right" way? Is it just a matter of taste, or are
> there implications about memory usage and optimisation according to
> which way you do it?

Note that we now have different semantics. The ASSOC approach will find 
the first match and provide only that help. So we have apples and 
oranges. But let's presume the spec is to find The One help message.

Aside from avoiding needless iteration past the match, the first two 
approaches collapse two steps (search and invocation) into one, reducing 
flexibility, maintainability, re-use, etc etc.

Mind you, this is not "Lisp advice", except in the sense that you have 
already discovered: Lisp offers so many built-in capabilities that we 
need to look to grander considerations to choose amongst multiple ways 
to solve a problem.

kt

-- 
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Cello? http://www.common-lisp.net/project/cello/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Your Project Here! http://alu.cliki.net/Industry%20Application
From: ·····@yagc.ndo.nospamplease.co.uk
Subject: Re: Bewildered Lisp newbie asks..is there a "right" way to code Lisp?
Date: 
Message-ID: <87u0wnoeie.fsf@yagc.ndo.co.uk>
Kenny Tilton <·······@nyc.rr.com> writes:

>
> Mind you, this is not "Lisp advice", except in the sense that you have
> already discovered: Lisp offers so many built-in capabilities that we
> need to look to grander considerations to choose amongst multiple ways
> to solve a problem.
>
> kt

Thanks to all who replied to this post. Things are a lot clearer
now. I settled on hashtables in the end, they seem to fit my purposes
best. They give me quick lookup, and I don't have to delve into
defmethod and the MOP just yet..;)

>
> -- 
> Home? http://tilton-technology.com
> Cells? http://www.common-lisp.net/project/cells/
> Cello? http://www.common-lisp.net/project/cello/
> Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
> Your Project Here! http://alu.cliki.net/Industry%20Application

John Connors
http://badbyteblues.blogspot.net
From: Wade Humeniuk
Subject: Re: Bewildered Lisp newbie asks..is there a "right" way to code Lisp?
Date: 
Message-ID: <f1fFc.41349$l6.23129@clgrps12>
And a Fourth Way:

(defmethod call-help ((name (eql 'one))) "This is help message one")
(defmethod call-help ((name (eql 'two))) "This is help message two")
(defmethod call-help ((name (eql 'hello))) "Hello world")
(defmethod call-help (name) nil)

CL-USER 3 > (call-help 'one)
"This is help message one"

CL-USER 4 > (call-help 'unknown)
NIL

CL-USER 5 >


Wade
From: Mark McConnell
Subject: Re: Bewildered Lisp newbie asks..is there a "right" way to code Lisp?
Date: 
Message-ID: <d3aed052.0407021736.197aa3f@posting.google.com>
Wade Humeniuk <····································@telus.net> wrote in message news:<····················@clgrps12>...
> And a Fourth Way:
> 
> (defmethod call-help ((name (eql 'one))) "This is help message one")

I'll present a "way" with hash-tables, which may be more efficient
than the original three if there are lots of topics.  [Though I think
Wade Humeniuk's is the more elegant.]

(defvar *help*
  (make-hash-table :size 1009))

(defun put-help (key help-func)
  (setf (gethash key *help*) help-func))

(defmacro awhen (test &body body) ;; after Graham, _On Lisp_
  `(let ((it ,test))
    (when it ,@body)))

(defun call-help (key)
  (awhen (gethash key *help*)
    (funcall it)))

(put-help 'one #'(lambda () "This is help message one."))

(call-help 'one) => "This is help message one."

(call-help 'unknown) => nil

(defun put-help-pending (key)
  (let ((str (format nil "Help for ~S is coming in release 3.6.5."
key)))
    (put-help key #'(lambda () str))))
               
(put-help-pending 'cow)

(call-help 'cow) => "Help for COW is coming in release 3.6.5."
From: Kenny Tilton
Subject: Re: Bewildered Lisp newbie asks..is there a "right" way to code Lisp?
Date: 
Message-ID: <4gqFc.47818$a92.27885@twister.nyc.rr.com>
Mark McConnell wrote:
> Wade Humeniuk <····································@telus.net> wrote in message news:<····················@clgrps12>...
> 
>>And a Fourth Way:
>>
>>(defmethod call-help ((name (eql 'one))) "This is help message one")
> 
> 
> I'll present a "way" with hash-tables, which may be more efficient
> than the original three if there are lots of topics.  [Though I think
> Wade Humeniuk's is the more elegant.]

It is a fun alternative, but unless one wants to do some hairy and 
non-standard MOP work, one does not end up with a database of all 
possible help messages. I know unix apps like to respond with all 
available help if they are asked for help on an unknown topic.

Here's a fifth alternative which, like assoc and hash tables, does 
establish a database as well as allow the lookup of specific help:

   (setf (get 'help 'lassie) "Water, Ken-l-ration, and lots of exercise")

with the database available en toto thusly:

   (symbol-plist 'help)

kt


-- 
Home? http://tilton-technology.com
Cells? http://www.common-lisp.net/project/cells/
Cello? http://www.common-lisp.net/project/cello/
Why Lisp? http://alu.cliki.net/RtL%20Highlight%20Film
Your Project Here! http://alu.cliki.net/Industry%20Application
From: Kalle Olavi Niemitalo
Subject: Re: Bewildered Lisp newbie asks..is there a "right" way to code Lisp?
Date: 
Message-ID: <87zn6degmp.fsf@Astalo.kon.iki.fi>
Wade Humeniuk <····································@telus.net> writes:

> (defmethod call-help (name) nil)

Perhaps (defmethod call-help ((name t)) nil) would be better,
to avoid any "unused variable" warnings.
From: Joe Marshall
Subject: Re: Bewildered Lisp newbie asks..is there a "right" way to code Lisp?
Date: 
Message-ID: <u0wqrx7f.fsf@ccs.neu.edu>
·····@yagc.ndo.co.uk writes:

> Then, yet a third way occured to me, assoc is made just for this kind
> of thing..
>
> The Third way.
> =============
>
> (defparameter helpers '((one . help-message-one) 
>                         (two . help-message-two) 
>                         (hello . hello-world)))
>
> (defun call-helper (name helper-list)
> 	   (let ((helper (cdr (assoc name helper-list))))
> 	     (if helper
> 		 (funcall helper))))
>
>
> CL-USER> (call-helper 'hello helpers)
> "Hello world"

This is the best among the three you posted.  Mapping is good for
transforming one list of elements into another, which isn't what you
were doing.  Assoc is for looking things up in a table, which is what
you were doing. 

> Is it just a matter of taste, or are there implications about memory
> usage and optimisation according to which way you do it?

That's an orthogonal issue.  The best performing way is to avoid a
linear search and use a hash table.  By writing your program as a
table lookup (assoc) you can now replace the table lookup sections
with hash lookups without rewriting large chunks of code.