Subject: Re: Style question
From: rpw3@rpw3.org (Rob Warnock)
Date: Tue, 06 Dec 2005 06:03:12 -0600
Newsgroups: comp.lang.lisp
Message-ID: <x4Kdna3blcSd4wjeRVn-sw@speakeasy.net>
John Thingstad <john.thingstad@chello.no> wrote:
+---------------
| micromoog <micromoog@gmail.com> wrote:
| > Is LOOP the way to go, then, for iteration?
| 
| Well. That is debatable.
| Some lispers avoid LOOP like the plaugue claiming it is unlispy.
| DO has a simple command structure that although difficult to read
| until you get used to it is far easier to get right.
| Now there is plenty of documentation on how to use LOOP right
| though so it is less of a problem.
| Still see www.norvig.com/luv-slides.ps for some style guides.
+---------------

Also, start slow with LOOP, sticking to a few simple templates
you understand [or useful idioms you see posted], and only add
additional refinements as you find you actually need them later.

A couple of my favorite basic LOOP-using functions [note the defaulting
of "FOR var = form" which LOOP treats as "FOR var = form THEN form"]:

    (defun file-lines (path)
      "Sucks up an entire file from PATH into a list of freshly-allocated
      strings, returning two values: the list of strings and the number of
      lines read."
      (with-open-file (s path)
	(loop for line = (read-line s nil nil)
	      while line
	  collect line into lines
	  counting t into line-count
	  finally (return (values lines line-count)))))

    (defun file-forms (path)
      "Sucks up an entire file from PATH into a list of forms (sexprs),
      returning two values: the list of forms and the number of forms read."
      (with-open-file (s path)
	(loop with eof = (list :eof)
	      for form = (read s nil eof)
	      until (eq form eof)
	  collect form into forms
	  counting t into form-count
	  finally (return (values forms form-count)))))

The most common set of simple "scanning" iterators in LOOP:

  - To scan a list, use "FOR item IN some-list ...".

  - To scan a string, use "FOR char ACROSS some-string ..."

  - To scan an alist, use "FOR (key . val) IN some-alist ..."

  - To scan a property list, use "FOR (prop val) ON some-plist BY #'cddr ..."

  - To scan a hash table, you need to type a somewhat longer template:

      FOR key BEING HASH-KEY IN some-hash-table USING (HASH-VALUE value) ...

Examples:

    > (let ((alist '((a . 5) (b . 3) (c . 17))))
	(loop FOR (key . val) IN alist
	  do (format t "~a = ~s~%" key val)))
    A = 5
    B = 3
    C = 17
    NIL
    > (let ((plist '(a 5 b 3 c 17)))
	(loop FOR (prop val) ON plist BY #'cddr   
	  do (format t "~a = ~s~%" prop val)))
    A = 5
    B = 3
    C = 17
    NIL
    > 


-Rob

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607