From: Don March on
On Nov 26, 5:32 am, p...(a)informatimago.com (Pascal J. Bourguignon)
wrote:
> Don March <don.ma...(a)gmail.com> writes:
> > A function I'm writing has a section like this:
>
> > (dolist (x long-list)
> >   (do-some-stuff)
> >   (push (if fn
> >        (funcall fn x)
> >        x)
> >    new-list))
>
> > Which is fine, but if long-list is actually long, it's silly and
> > possibly slow to evaluate the "(if fn..." stuff every time.  I came up
> > with the following 2.5 alternatives:
>
> > ;; option 1 (probably implemented with macros to avoid duplicate code)
> > (if fn
> >     (dolist (x long-list)
> >       (do-some-stuff)
> >       (push (funcall fn x) new-list))
> >     (dolist (x long-list)
> >       (do-some-stuff)
> >       (push x new-list)))
>
> Notice that any compiler worth its bits will do that transformation
> automatically (moving constant computations outside of loops), perhaps
> with (declaim (optimize (speed 3) (space 1))).
>
> Otherwise if you have to do it yourself, use a macrolet as indicated
> by joswig.
>
> --
> __Pascal Bourguignon__

After experimenting a bit, it seems SBCL runs both versions at about
the same speed. CLISP runs the code with the constant computations
moved outside the loop in about 94% of the time as the original code,
no matter what optimization settings I try.

The code wasn't really a bottleneck, I'm just trying to learn to write
lisp in a way that wouldn't make experts cringe. (Apparently the
"ugly and slow" eval version would fall in that category :) )

Thanks for your help, everyone.
From: Alberto Riva on
Don March wrote:
> On Nov 26, 5:32 am, p...(a)informatimago.com (Pascal J. Bourguignon)
> wrote:
>> Don March <don.ma...(a)gmail.com> writes:
>>> A function I'm writing has a section like this:
>>> (dolist (x long-list)
>>> (do-some-stuff)
>>> (push (if fn
>>> (funcall fn x)
>>> x)
>>> new-list))
>>> Which is fine, but if long-list is actually long, it's silly and
>>> possibly slow to evaluate the "(if fn..." stuff every time. I came up
>>> with the following 2.5 alternatives:
>>> ;; option 1 (probably implemented with macros to avoid duplicate code)
>>> (if fn
>>> (dolist (x long-list)
>>> (do-some-stuff)
>>> (push (funcall fn x) new-list))
>>> (dolist (x long-list)
>>> (do-some-stuff)
>>> (push x new-list)))
>> Notice that any compiler worth its bits will do that transformation
>> automatically (moving constant computations outside of loops), perhaps
>> with (declaim (optimize (speed 3) (space 1))).
>>
>> Otherwise if you have to do it yourself, use a macrolet as indicated
>> by joswig.
>>
>> --
>> __Pascal Bourguignon__
>
> After experimenting a bit, it seems SBCL runs both versions at about
> the same speed. CLISP runs the code with the constant computations
> moved outside the loop in about 94% of the time as the original code,
> no matter what optimization settings I try.
>
> The code wasn't really a bottleneck, I'm just trying to learn to write
> lisp in a way that wouldn't make experts cringe. (Apparently the
> "ugly and slow" eval version would fall in that category :) )

Yes, but not just because it's ugly and slow: more importantly, it won't
work. EVAL evaluates its argument in an empty lexical environment, which
means that your variables `fn' and `x' won't be visible.

Alberto