From: frito
Subject: Lost in Loop
Date: 
Message-ID: <1144760154.590235.209380@i39g2000cwa.googlegroups.com>
Hi,

As will become obvious, I am new to Lisp and especially to Loop; hope
this
is the right group to ask the question.

Someone gave me a simple paper
puzzle(http://home.snu.edu/~hculbert/answer.htm) and I decided to use
it to learn Lisp.  Basically, for a list of book titles I wanted to
return a list of position in text and the book title, so
(build-book-index "wjhedbwjhbMarkajbdadbGenesis" *books*)

gave: ((21 "Genesis") (10 "Mark")).

I had an approach working but it looked horrid so I thought I would try
loop.  I came up with this:

(defun build-book-index (text books)
 (loop for book in books
    when (search (string-downcase book) (string-downcase text)) collect
    (list it book)))

Of course, ...collect it ... worked but the version above gave:

in: LAMBDA NIL
;     (LIST IT BOOK)
; caught WARNING:  undefined variable: IT

Is this expected?  How can I get round it? To me this looks so great
I'd
love it or something similar to work!!

Comments, pointers, thoughts welcome.

Cheers,
Keith

From: Pascal Bourguignon
Subject: Re: Lost in Loop
Date: 
Message-ID: <87acasmevp.fsf@thalassa.informatimago.com>
"frito" <············@gmail.com> writes:

> Hi,
>
> As will become obvious, I am new to Lisp and especially to Loop; hope
> this
> is the right group to ask the question.
>
> Someone gave me a simple paper
> puzzle(http://home.snu.edu/~hculbert/answer.htm) and I decided to use
> it to learn Lisp.  Basically, for a list of book titles I wanted to
> return a list of position in text and the book title, so
> (build-book-index "wjhedbwjhbMarkajbdadbGenesis" *books*)
>
> gave: ((21 "Genesis") (10 "Mark")).
>
> I had an approach working but it looked horrid so I thought I would try
> loop.  I came up with this:
>
> (defun build-book-index (text books)
>  (loop for book in books
>     when (search (string-downcase book) (string-downcase text)) collect
>     (list it book)))
>
> Of course, ...collect it ... worked but the version above gave:
>
> in: LAMBDA NIL
> ;     (LIST IT BOOK)
> ; caught WARNING:  undefined variable: IT
>
> Is this expected?  

Yes.

> How can I get round it? 

Not using IT.

(defun build-book-index (text books)
  (loop
     for book in books
     for my-it = (search (string-downcase book) (string-downcase text))
     when my-it collect (list my-it book)))


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

READ THIS BEFORE OPENING PACKAGE: According to certain suggested
versions of the Grand Unified Theory, the primary particles
constituting this product may decay to nothingness within the next
four hundred million years.
From: frito
Subject: Re: Lost in Loop
Date: 
Message-ID: <1144762819.930414.119050@e56g2000cwe.googlegroups.com>
Pascal,

Thanks for the fast response.  It works, and I'm dumbfounded because I
thought of that but in my case my-it ( I called it book-index) was
always "nil" but then I used "with" and not "for".  Obviously I have
not interpreted CLHS correctly.

BTW why is "it" unbound in my example? I think I am missing something
important.

Thanks again,
Keith
From: jayessay
Subject: Re: Lost in Loop
Date: 
Message-ID: <m31ww45e8x.fsf@rigel.goldenthreadtech.com>
"frito" <············@gmail.com> writes:

> BTW why is "it" unbound in my example? I think I am missing something
> important.

In LOOP land, IT is taken as a keyword, not a variable.


/Jon

-- 
'j' - a n t h o n y at romeo/charley/november com
From: jayessay
Subject: Re: Lost in Loop
Date: 
Message-ID: <m3wtdw3zgw.fsf@rigel.goldenthreadtech.com>
jayessay <······@foo.com> writes:

> "frito" <············@gmail.com> writes:
> 
> > BTW why is "it" unbound in my example? I think I am missing something
> > important.
> 
> In LOOP land, IT is taken as a keyword, not a variable.
                                 ^^^^^^^

Sorry, but I should have been more specific here: that should be _loop
keyword_, which is _not_ a keyword (as in symbol in keyword package),
but rather as a special name guiding parsing (as in keywords/reserved
words in typical programming languages).


/Jon

-- 
'j' - a n t h o n y at romeo/charley/november com
From: Pascal Bourguignon
Subject: Re: Lost in Loop
Date: 
Message-ID: <87y7yblr32.fsf@thalassa.informatimago.com>
jayessay <······@foo.com> writes:

> jayessay <······@foo.com> writes:
>
>> "frito" <············@gmail.com> writes:
>> 
>> > BTW why is "it" unbound in my example? I think I am missing something
>> > important.
>> 
>> In LOOP land, IT is taken as a keyword, not a variable.
>                                  ^^^^^^^
>
> Sorry, but I should have been more specific here: that should be _loop
> keyword_, which is _not_ a keyword (as in symbol in keyword package),
> but rather as a special name guiding parsing (as in keywords/reserved
> words in typical programming languages).

Well, of course, IT can be :IT 

(loop :for i :in '( a b nil c nil d) :when i :collect :it) --> (A B C D)


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

ADVISORY: There is an extremely small but nonzero chance that,
through a process known as "tunneling," this product may
spontaneously disappear from its present location and reappear at
any random place in the universe, including your neighbor's
domicile. The manufacturer will not be responsible for any damages
or inconveniences that may result.
From: jayessay
Subject: Re: Lost in Loop
Date: 
Message-ID: <m3sloi4t3g.fsf@rigel.goldenthreadtech.com>
Pascal Bourguignon <······@informatimago.com> writes:

> jayessay <······@foo.com> writes:
> 
> > jayessay <······@foo.com> writes:
> >
> >> "frito" <············@gmail.com> writes:
> >> 
> >> > BTW why is "it" unbound in my example? I think I am missing something
> >> > important.
> >> 
> >> In LOOP land, IT is taken as a keyword, not a variable.
> >                                  ^^^^^^^
> >
> > Sorry, but I should have been more specific here: that should be _loop
> > keyword_, which is _not_ a keyword (as in symbol in keyword package),
> > but rather as a special name guiding parsing (as in keywords/reserved
> > words in typical programming languages).
> 
> Well, of course, IT can be :IT 

True, but irrelevant to the point.


/Jon

-- 
'j' - a n t h o n y at romeo/charley/november com
From: David Sletten
Subject: Re: Lost in Loop
Date: 
Message-ID: <3x%_f.1439$543.1038@tornado.socal.rr.com>
Pascal Bourguignon wrote:

> 
> (defun build-book-index (text books)
>   (loop
>      for book in books
>      for my-it = (search (string-downcase book) (string-downcase text))
>      when my-it collect (list my-it book)))
> 
> 

Don't forget to downcase just once:
(defun build-book-index (text books)
   (let ((search-text (string-downcase text)))
     (loop for book in books
           for my-it = (search (string-downcase book) search-text)
           when my-it collect (list my-it book))))

Aloha,
David Sletten
From: frito
Subject: Re: Lost in Loop
Date: 
Message-ID: <1144829317.420217.123160@z34g2000cwc.googlegroups.com>
Hi, David,

Good point - that will speed it up.

Could your "let" clause be replaced with an  "initially" clause in the
loop?  Or a "with"?

I still haven't quite groked the difference with these :-(

Keith

David Sletten wrote:
>
> Don't forget to downcase just once:
> (defun build-book-index (text books)
>    (let ((search-text (string-downcase text)))
>      (loop for book in books
>            for my-it = (search (string-downcase book) search-text)
>            when my-it collect (list my-it book))))
> 
> Aloha,
> David Sletten
From: Joe Marshall
Subject: Re: Lost in Loop
Date: 
Message-ID: <1144861059.991359.135890@u72g2000cwu.googlegroups.com>
frito wrote:
> Hi, David,
>
> Good point - that will speed it up.
>
> Could your "let" clause be replaced with an  "initially" clause in the
> loop?  Or a "with"?
>
> I still haven't quite groked the difference with these :-(

The hyperspec will tell you.

A `with' clause introduces a `let' binding.  Multiple `with' clauses
work like `let*'.  An `and' clause makes the binding happen in parallel
like a regular `let'.

`Initially' introduces a form to be executed after the bindings, but
before the iteration.

BUT....

There isn't enough knee-jerk anti-loopism in this group.  LOOP is hard
to deal with
when you are writing programs that analyze other programs.  Yes, you
can macroexpand
it away, but the resulting code is no easier to analyze:

(MACROLET ((LOOP-FINISH () '(GO #:|end-loop-893|)))
  (COMMON-LISP:LET ((BOOK NIL)
                    (#:|tail-896| BOOKS)
                    (#:|by-897| 'SYSTEM:CDR$CONS))
    (COMMON-LISP:LET ((SEARCH-TEXT (STRING-DOWNCASE TEXT)))
      (COMMON-LISP:LET ((MY-IT NIL))
        (COMMON-LISP:LET ((#:|accumulator-894| (LIST NIL)))
          (DECLARE (TYPE LIST #:|accumulator-894|))
          (COMMON-LISP:LET ((#:|aux-var-901| #:|accumulator-894|))
            (BLOCK NIL
              (TAGBODY
               #:|begin-loop-892| (PROGN
                                   (WHEN
                                    (OR (ENDP #:|tail-896|))
                                    (GO #:|end-loop-893|))
                                   (COMMON-LISP:LET
                                    ((#:|temp-899|
                                      (COMMON-LISP:FUNCALL
                                       #:|by-897|
                                       #:|tail-896|))
                                     (#:|temp-898|
                                      (SYSTEM:CAR$CONS #:|tail-896|)))
                                    (SETQ BOOK #:|temp-898|)
                                    (SETQ #:|tail-896| #:|temp-899|)))
                       (PROGN
                         (COMMON-LISP:LET ((#:|temp-900|
                                            (SEARCH
                                             (STRING-DOWNCASE BOOK)
                                             SEARCH-TEXT)))
                           (SETQ MY-IT #:|temp-900|)))
                       (COMMON-LISP:LET ((LOOP::IT MY-IT))
                         (IF LOOP::IT
                             (PROGN
                               (SETQ
                                #:|aux-var-901|
                                (LAST
                                 (RPLACD
                                  #:|aux-var-901|
                                  (LIST (LIST MY-IT BOOK))))))
                           (PROGN)))
                       (GO #:|begin-loop-892|)
               #:|end-loop-893| (RETURN-FROM
                                 NIL
                                 (CDR #:|accumulator-894|))))))))))

I just don't like LOOP.  Here are some non-loop alternatives:

(defun build-book-index-1 (text books)
  (let ((search-text (string-downcase text)))
    (mapcan (lambda (book)
              (let ((position (search search-text
                                      (string-downcase book))))
                (when position
                  (list (list position book)))))
            books)))

;;; SERIES version
(defun build-book-index-2 (text books)
  (let ((search-text (string-downcase text)))
    (collect 'list
      (choose-if
       (lambda (entry)
         (numberp (car entry)))
       (map-fn 'list
               (lambda (book)
                 (list (search search-text (string-downcase book))
                       book))
               (scan 'list books))))))

(defun build-book-index-3 (text books)
  (let ((search-text (string-downcase text)))
    (remove-if-not #'numberp
                   (map 'list (lambda (book)
                                (list (search search-text
                                              (string-downcase book))
                                      book))
                        books)
                   :key #'car)))

(defun build-book-index-4 (text books)
  (let ((search-text (string-downcase text)))
    (do ((books books (cdr books))
         (index '() (let* ((book (car books))
                           (position (search search-text
                                            (string-downcase book))))
                      (if position
                          (cons (list position book) index)
                           index))))
        ((null books) (nreverse index)))))
From: David Sletten
Subject: Re: Lost in Loop
Date: 
Message-ID: <b%5%f.1801$3W1.1286@tornado.socal.rr.com>
frito wrote:

> Could your "let" clause be replaced with an  "initially" clause in the
> loop?  Or a "with"?

WITH will work:
(defun build-book-index (text books)
   (loop for book in books
         with search-text = (string-downcase text)
         for my-it = (search (string-downcase book) search-text)
         when my-it collect (list my-it book)))

Aloha,
David Sletten
From: Wade Humeniuk
Subject: Re: Lost in Loop
Date: 
Message-ID: <6sg%f.71335$%H.533@clgrps13>
David Sletten wrote:
> Pascal Bourguignon wrote:
> 
>>
>> (defun build-book-index (text books)
>>   (loop
>>      for book in books
>>      for my-it = (search (string-downcase book) (string-downcase text))
>>      when my-it collect (list my-it book)))
>>
>>
> 
> Don't forget to downcase just once:
> (defun build-book-index (text books)
>   (let ((search-text (string-downcase text)))
>     (loop for book in books
>           for my-it = (search (string-downcase book) search-text)
>           when my-it collect (list my-it book))))

Its easier just to use the :test argument in search

(defun build-book-index (text books)
   (loop for book in books
         for my-it = (search book text :test #'string-equal)
         when my-it collect (list my-it book)))

Wade
From: David Sletten
Subject: Re: Lost in Loop
Date: 
Message-ID: <ZZi%f.1964$3W1.504@tornado.socal.rr.com>
Wade Humeniuk wrote:
> David Sletten wrote:
> 
>> Pascal Bourguignon wrote:
>>
>>>
>>> (defun build-book-index (text books)
>>>   (loop
>>>      for book in books
>>>      for my-it = (search (string-downcase book) (string-downcase text))
>>>      when my-it collect (list my-it book)))
>>>
>>>
>>
>> Don't forget to downcase just once:
>> (defun build-book-index (text books)
>>   (let ((search-text (string-downcase text)))
>>     (loop for book in books
>>           for my-it = (search (string-downcase book) search-text)
>>           when my-it collect (list my-it book))))
> 
> 
> Its easier just to use the :test argument in search
> 
> (defun build-book-index (text books)
>   (loop for book in books
>         for my-it = (search book text :test #'string-equal)
>         when my-it collect (list my-it book)))
> 
> Wade

Easier? Yes. As efficient? No. Now on each iteration SEARCH has to 
perform 2 tests at each position (uppercase match? lowercase match?) to 
attempt to locate the subsequence. Better to just downcase the whole 
search text once and rely on a simpler test.

Of course, this decision must be made in context. Here your simpler 
solution will still be adequately fast, and mine requires additional memory.

Aloha,
David Sletten