From: normanj
Subject: need help for a simple macro
Date: 
Message-ID: <f197ad95-5480-4989-bfe2-3205ddb9d538@71g2000hse.googlegroups.com>
I want a macro in the form:
(myprint 'a 'b 'c 'd)
expand to:
(progn  (print 'a)  (print 'b) (print 'c) (print 'd))

I know it's simple, but I can't work it out. Could anybody help me?

Thanks.

From: William D Clinger
Subject: Re: need help for a simple macro
Date: 
Message-ID: <e0a01feb-d2c9-4145-8dbf-3bd73b8d4c2c@q78g2000hsh.googlegroups.com>
normanj wrote:
> I want a macro in the form:
> (myprint 'a 'b 'c 'd)
> expand to:
> (progn  (print 'a)  (print 'b) (print 'c) (print 'd))

I'd define it like this:

    (define-syntax myprint
      (syntax-rules ()
       ((myprint exp1 ...)
        (progn (print exp1) ...))))

That's written in a benighted language, so you'll
have to simplify it before it will work in Common
Lisp.

Will
From: Peter Hildebrandt
Subject: Re: need help for a simple macro
Date: 
Message-ID: <47bc7234$0$90275$14726298@news.sunsite.dk>
normanj wrote:
> I want a macro in the form:
> (myprint 'a 'b 'c 'd)
> expand to:
> (progn  (print 'a)  (print 'b) (print 'c) (print 'd))
> 
> I know it's simple, but I can't work it out. Could anybody help me?

If you're looking for a quick solution, start playing with this:

(defun make-prints (variables)
   (append '(progn)
           (mapcar #'(lambda (var) (list 'print var)) variables)))

(make-prints '(a b c))
==> (PROGN (PRINT A) (PRINT B) (PRINT C))

Your next tasks:
- understand mapcar
- transform the function into a macro
- introduce &rest in the parameter list

The next time you need to do CS homework, check Peter Seibel's book 
"Practical Common Lisp" first.  You can read it online for free.

Peter

> 
> Thanks.
From: normanj
Subject: Re: need help for a simple macro
Date: 
Message-ID: <14baaf4e-c2c1-4f62-a0fe-625a79357c7d@q33g2000hsh.googlegroups.com>
> If you're looking for a quick solution, start playing with this:
>
> (defun make-prints (variables)
>    (append '(progn)
>            (mapcar #'(lambda (var) (list 'print var)) variables)))
>
> (make-prints '(a b c))
> ==> (PROGN (PRINT A) (PRINT B) (PRINT C))

Thanks you! This solution should solve my problem -- the real problem
is more complicated than the above.
I'm not totally sure about the power of Lisp macros, and why it's so
powerful. So, I got stuck with it.

Anyway, is it possible to solve it like this?
(defmacro pattern (&body body)
  (loop for item in body collect
	(let ((condt  (first  item))
	      (action (second item)))
	  `(if ,condt
	    ,action
	    nil))))
The problem with this solution is that the "progn" cannot be inserted
into the result.

What I want is to generate codes like this:
(progn
  (if condition action nil)
  (if condition action nil)
  ...)


> Your next tasks:
> - understand mapcar
> - transform the function into a macro
> - introduce &rest in the parameter list
>
> The next time you need to do CS homework, check Peter Seibel's book
> "Practical Common Lisp" first.  You can read it online for free.

I'm reading Peter's book.

Best regards,
Norman
From: danb
Subject: Re: need help for a simple macro
Date: 
Message-ID: <52e273f6-e76d-4480-93f0-5e90f358f34c@e10g2000prf.googlegroups.com>
On Feb 20, 1:29 pm, normanj <·······@gmail.com> wrote:
> Anyway, is it possible to solve it like this?
> (defmacro pattern (&body body)
>   (loop for item in body collect
>         (let ((condt  (first  item))
>               (action (second item)))
>           `(if ,condt
>             ,action
>             nil))))
> The problem with this solution is that the "progn" cannot be inserted
> into the result.

Sure it can.  Just put APPEND 'PROGN before the LOOP form.

--Dan
www.prairienet.org/~dsb/
From: Peter Hildebrandt
Subject: Re: need help for a simple macro
Date: 
Message-ID: <47bc9b8c$0$90270$14726298@news.sunsite.dk>
normanj wrote:
>> If you're looking for a quick solution, start playing with this:
>>
>> (defun make-prints (variables)
>>    (append '(progn)
>>            (mapcar #'(lambda (var) (list 'print var)) variables)))
>>
>> (make-prints '(a b c))
>> ==> (PROGN (PRINT A) (PRINT B) (PRINT C))
> 
> Thanks you! This solution should solve my problem -- the real problem
> is more complicated than the above.
> I'm not totally sure about the power of Lisp macros, and why it's so
> powerful. So, I got stuck with it.

Well, plenty of people seem to be content using languages without 
macros, such as C, C++, Java, or Python.  So at least you're not alone. 
  But if you keep working you'll see the light :-)

> Anyway, is it possible to solve it like this?
> (defmacro pattern (&body body)
>   (loop for item in body collect
> 	(let ((condt  (first  item))
> 	      (action (second item)))
> 	  `(if ,condt
> 	    ,action
> 	    nil))))

Looks good to me, given your body is a list of lists:
   ((cond1 act1) (cond2 act2) ...)

You could clean up the code if you get rid of the let and use
   `(if ,(first item) ...)

You could get rid of some of the parens and use a setf-style argument 
list if you like:
   (loop for (cond action) on body by #'cddr
     collect ...)

would accept (cond1 act1 cond2 act2)

> The problem with this solution is that the "progn" cannot be inserted
> into the result.

Dan already pointed out to use append.

> What I want is to generate codes like this:
> (progn
>   (if condition action nil)
>   (if condition action nil)
>   ...)

(if <condition> <action> nil) has a name, btw:  "when"

>> Your next tasks:
>> - understand mapcar
>> - transform the function into a macro
>> - introduce &rest in the parameter list
>>
>> The next time you need to do CS homework, check Peter Seibel's book
>> "Practical Common Lisp" first.  You can read it online for free.
> 
> I'm reading Peter's book.

Good.

Peter

> Best regards,
> Norman
From: normanj
Subject: Re: need help for a simple macro
Date: 
Message-ID: <e29c54da-cb80-4f81-8d83-c756cf5e3c9e@e60g2000hsh.googlegroups.com>
> Looks good to me, given your body is a list of lists:
>    ((cond1 act1) (cond2 act2) ...)
>
> You could clean up the code if you get rid of the let and use
>    `(if ,(first item) ...)
>
> You could get rid of some of the parens and use a setf-style argument
> list if you like:
>    (loop for (cond action) on body by #'cddr
>      collect ...)
>
> would accept (cond1 act1 cond2 act2)

Wonderful!

> > The problem with this solution is that the "progn" cannot be inserted
> > into the result.
>
> Dan already pointed out to use append.

Yes, thanks all.

> (if <condition> <action> nil) has a name, btw:  "when"
>

In fact, the "nil" here is something else, not nil.
The final codes look like:
  (progn (if cond1 act1 something-else)
         (if cond2 act2 something-else)
         ... )
The "something-else" of all "if" are the same.
From: Peter Hildebrandt
Subject: Re: need help for a simple macro
Date: 
Message-ID: <47bd425d$0$90266$14726298@news.sunsite.dk>
normanj wrote:
>> (if <condition> <action> nil) has a name, btw:  "when"
>>
> 
> In fact, the "nil" here is something else, not nil.
> The final codes look like:
>   (progn (if cond1 act1 something-else)
>          (if cond2 act2 something-else)
>          ... )
> The "something-else" of all "if" are the same.
>

So you have something-else evaluated for every cond which is not met? 
Or are you looking for

(cond
   (cond1 act1)
   (cond2 act2)
   (t something-else))

Btw, there's a lesson here:  Usually you get better responses on c.l.l 
if you post a more complete version of the problem along with the code 
that you already have.  (Otherwise it looks like someone just wants 
their homework done for them ;-))

Peter
From: Ken Tilton
Subject: Re: need help for a simple macro
Date: 
Message-ID: <47bcc765$0$25033$607ed4bc@cv.net>
Peter Hildebrandt wrote:
> normanj wrote:
> 
>>> If you're looking for a quick solution, start playing with this:
>>>
>>> (defun make-prints (variables)
>>>    (append '(progn)
>>>            (mapcar #'(lambda (var) (list 'print var)) variables)))
>>>
>>> (make-prints '(a b c))
>>> ==> (PROGN (PRINT A) (PRINT B) (PRINT C))
>>
>>
>> Thanks you! This solution should solve my problem -- the real problem
>> is more complicated than the above.
>> I'm not totally sure about the power of Lisp macros, and why it's so
>> powerful. So, I got stuck with it.
> 
> 
> Well, plenty of people seem to be content using languages without 
> macros, such as C...

You should see some of my C macros. And I guess others were as metaprone 
as I cuz Java had "First, we kill all the macros" as a Prime Directive.

Now if you want to talk about the project where I used Cobol's 
copy-replacing to befuddle my PHB... I guess there is content and then 
there is content.

I have said before, it comes down to laziness. Lisp macros are for 
people who are always on the lookout for ways to get out of working. 
Like comp.lang.lisp and the release of a new language like Arc, I 
haven't had to work on that Algebra software in a week.

Gotta run, American Idol is coming on...

kenny

-- 
http://smuglispweeny.blogspot.com/
http://www.theoryyalgebra.com/

"In the morning, hear the Way;
  in the evening, die content!"
                     -- Confucius
From: Kent M Pitman
Subject: Re: need help for a simple macro
Date: 
Message-ID: <u63wj6wir.fsf@nhplace.com>
Peter Hildebrandt <·················@gmail.com> writes:

>    (append '(progn)

Any time you find yourself writing

 (append '(just-one-thing) something-else)

this, you might consider

 (cons 'just-one-thing something-else)

APPEND is something to avoid where you can anyway, and which raises a
red flag to people looking at your code who worry about excess 
algorithmic complexity... in this case needlessly.
From: Peter Hildebrandt
Subject: Re: need help for a simple macro
Date: 
Message-ID: <47bd4496$0$90266$14726298@news.sunsite.dk>
Kent M Pitman wrote:
> Peter Hildebrandt <·················@gmail.com> writes:
> 
>>    (append '(progn)
> 
> Any time you find yourself writing
> 
>  (append '(just-one-thing) something-else)
> 
> this, you might consider
> 
>  (cons 'just-one-thing something-else)

Yep, in this case that would be perfectly fine.  Actually, I started 
writing cons, but since the OP's problem formulation was not exactly 
precise, I figured I'd go with the more general append so he could 
easily stick more forms in there and see what happens.

> APPEND is something to avoid where you can anyway, and which raises a
> red flag to people looking at your code who worry about excess 
> algorithmic complexity... in this case needlessly.

True.  I avoid it whenever I can.  I am a little less concerned about it 
in macro code, though, since it does not affect runtime behavior.

Besides, in this case I would have assumed my compiler would be smart 
enough to transform the append into a cons given that the first arg is a 
literal constant.  And then the cost of using append in a macro appeared 
small to me compared to the win in flexibility for the user who can more 
easily modify the code template.

Anyway, I take your advice seriously, and I repent, and I will go 
through my project tonight and track down unnecessary uses of append in 
macro definitions.

Peter
From: Kent M Pitman
Subject: Re: need help for a simple macro
Date: 
Message-ID: <uy79egxgr.fsf@nhplace.com>
Peter Hildebrandt <·················@gmail.com> writes:

> Kent M Pitman wrote:
> > Peter Hildebrandt <·················@gmail.com> writes:
> >
> >>    (append '(progn)
> > Any time you find yourself writing
> >  (append '(just-one-thing) something-else)
> > this, you might consider
> >  (cons 'just-one-thing something-else)
> 
> Yep, in this case that would be perfectly fine.  Actually, I started
> writing cons, but since the OP's problem formulation was not exactly
> precise, I figured I'd go with the more general append so he could
> easily stick more forms in there and see what happens.

Yeah, that kind of makes sense in that context.  Although hopefully he'll
only copy the idea there and not inside a loop at runtime of a serious
program. :)
 
> > APPEND is something to avoid where you can anyway, and which raises a
> > red flag to people looking at your code who worry about excess
> > algorithmic complexity... in this case needlessly.
> 
> True.  I avoid it whenever I can.  I am a little less concerned about
> it in macro code, though, since it does not affect runtime behavior.

As I always say about style rules:  they are guidelines, not really rules.
It's more important why you do something than that you do something.
This seems a perfectly acceptable thing.
 
> Besides, in this case I would have assumed my compiler would be smart
> enough to transform the append into a cons given that the first arg is
> a literal constant.  And then the cost of using append in a macro
> appeared small to me compared to the win in flexibility for the user
> who can more easily modify the code template.

I suspect most compilers will optimize this, and you're right it
likely doesn't matter.  I certainly did not mention it because of a
strict speed issue.  Really it's because people should try to know the
algorithmic complexity of their code in Big-O notation, and APPEND is
typically an O(n) thing and CONS is typically an O(1) thing.  With
constant args, especially a constant 1, that's not true, of course,
but then, big-O notation doesn't always tell you the full story since
it ignores even huge constant factors.  The real "danger" is not that
the use of APPEND is wrong but that the eye can only be drawn to so
many things before it becomes weary.

The relevant teaching parable here is "the boy who cried wolf" story.
Using APPEND is like crying wolf.  It may be there is no wolf.  But
that doesn't mean there's no cost.  It may be that some day APPEND is
used badly and you're used to ignoring it... that's the cost I really
meant to point to, if that makes any sense.

> Anyway, I take your advice seriously, and I repent, and I will go
> through my project tonight and track down unnecessary uses of append
> in macro definitions.

I'm not trying to promote religious dogma, just careful thought.

In my view, two wise people may, through careful thought, reach
different conclusions that are nevertheless wise decisions.  And two
thoughtless or unwise people may reach the same conclusion out of 
randomness, and that doesn't make the decision wise.  The issue isn't
the outcome, it's the process.

As long as you're thinking about why you're doing things, and not just
making one-size-fits-all rules, I'm ok with it.

Plus it's not like you have to satisfy me anyway.  But you knew that.
I just wanted to say I knew it, too.
From: Peter Hildebrandt
Subject: Re: need help for a simple macro
Date: 
Message-ID: <47bd9c6d$0$90266$14726298@news.sunsite.dk>
Kent,

Thank you for your comprehensive reply.  I am pretty sure everything 
that can be said on the issue has been said now, but I'd like to add a 
few closing remarks.

Kent M Pitman wrote:
> Peter Hildebrandt <·················@gmail.com> writes:
>> Yep, in this case that would be perfectly fine.  Actually, I started
>> writing cons, but since the OP's problem formulation was not exactly
>> precise, I figured I'd go with the more general append so he could
>> easily stick more forms in there and see what happens.
> 
> Yeah, that kind of makes sense in that context.  Although hopefully he'll
> only copy the idea there and not inside a loop at runtime of a serious
> program. :)

Very true.  Indeed you often see people using append gratuitously when 
they start programming in lisp, and is not exactly wise to support that 
habit.  Why is that?  Maybe because append is among the first lisp 
operations people are taught in CS classes?  I don't know.

> Really it's because people should try to know the
> algorithmic complexity of their code in Big-O notation, and APPEND is
> typically an O(n) thing and CONS is typically an O(1) thing.  With
> constant args, especially a constant 1, that's not true, of course,
> but then, big-O notation doesn't always tell you the full story since
> it ignores even huge constant factors.  The real "danger" is not that
> the use of APPEND is wrong but that the eye can only be drawn to so
> many things before it becomes weary.

This is a true point.  Non-CS people (or undergraduate students) tend to 
ignore the complexity of their algorithms, and write O(n^2) where O(n) 
would do.  The justification that the "hardware is fast enough" or that 
"the compiler should catch it" is not exactly helpful.

As a general rule you are most certainly right.  It is better to watch 
out for insufficient code where it si not exactly necessary, than to 
become accustomed to writing inefficient code.  Plus, it is always a 
good mental exercise to see whether one's code could easily be optimised.

> The relevant teaching parable here is "the boy who cried wolf" story.
> Using APPEND is like crying wolf.  It may be there is no wolf.  But
> that doesn't mean there's no cost.  It may be that some day APPEND is
> used badly and you're used to ignoring it... that's the cost I really
> meant to point to, if that makes any sense.

Yep, got it.  Especially in an answer to a beginner's question one 
should be aware that pointing a beginner to "append" might start a bad 
habit.

>> Anyway, I take your advice seriously, and I repent, and I will go
>> through my project tonight and track down unnecessary uses of append
>> in macro definitions.
> 
> I'm not trying to promote religious dogma, just careful thought.

Obviously (I hope) it is clear that I was joking.  But there was a grain 
of truth to it:  (append `(f ,a) ..) and `(append (f ,a) ...) look so 
similar that I see the point in avoiding the former case even though it 
does not entail a performance penalty.  As you said, once you are used 
to the former, you might fail to detect the latter.

> Plus it's not like you have to satisfy me anyway.  But you knew that.
> I just wanted to say I knew it, too.

Well, I have learned a bit from this exchange, so I think it was very 
well worth it.  Thanks a lot!

Peter
From: Andrew Reilly
Subject: Re: need help for a simple macro
Date: 
Message-ID: <626l6pF2227ksU1@mid.individual.net>
On Thu, 21 Feb 2008 16:44:49 +0100, Peter Hildebrandt wrote:

> Indeed you often see people using append gratuitously when they start
> programming in lisp, and is not exactly wise to support that habit.  Why
> is that?  Maybe because append is among the first lisp operations people
> are taught in CS classes?  I don't know.

My own experience is unlikely to be typical, but I'll just point out, 
here, that it is append, rather than cons, that is the O(1) "fast" list 
building technique in a lot of popular non-lisp languages, where lists 
are built with resizable vectors, rather than single links.  Even when 
there are links, the end of the list is often available O(1), because the 
list is enclosed in an object that is keeping track of it.  Idioms don't 
necessarily translate as readily as basic vocabulary.

Also, it goes with the right-to-left reading/writing direction, perhaps.

I believe that I'm getting better, though.  My code has not many appends 
in it, now.

Cheers,

-- 
Andrew
From: Andrew Reilly
Subject: Re: need help for a simple macro
Date: 
Message-ID: <626lrgF2227ksU3@mid.individual.net>
On Fri, 22 Feb 2008 01:01:14 +0000, Andrew Reilly wrote:

> Also, it goes with the right-to-left reading/writing direction, perhaps.

Clearly, I meant left-to-right, here, since that's what I was using :-)

Cheers,

-- 
Andrew
From: Kaz Kylheku
Subject: Re: need help for a simple macro
Date: 
Message-ID: <5c94b8f3-178d-40eb-82cd-f8105146c4e2@s37g2000prg.googlegroups.com>
On Feb 20, 9:45 am, normanj <·······@gmail.com> wrote:
> I want a macro in the form:
> (myprint 'a 'b 'c 'd)
> expand to:
> (progn  (print 'a)  (print 'b) (print 'c) (print 'd))

This only has to be a macro if any these requirements exist:

- if one of the forms performs a nonlocal exit, the values of any
previous
  forms are still printed; or

- the value of each form is to be printed as soon as possible, prior
to the
  evaluation of the next form, because the forms themselves have side
effects
  which interact with printing.

Otherwise:

  (defun print-all (&rest args)
    (mapc #'print args))

I'd give a different name to the macro version, like CHATTY-PROGN. The
macro version should handle multiple values from each form, in
particular the last one. I'd make it like this:

  (defmacro chatty-progn (&body forms)
    (let ((lv-sym (gensym "LAST-VALUES"))
          (rt-sym (gensym "RETURNED")))
      `(let (,lv-sym)
         ,@(loop for form in forms
                 collect
                 `(let ((,rt-sym nil))
                    (unwind-protect
                       (progn
                         (setf ,lv-sym (multiple-value-list ,form))
                         (setf ,rt-sym t))
                       (if ,rt-sym
                         (format t "form ~s returned ~s~%"
                                 ',form
                                 `(values ,@,lv-sym))
                         (format t "form ~s jumped out~%" ',form)))))
          (values-list ,lv-sym))))

Tests:

  > (chatty-progn)
  ;; nothing

This is different from (progn) which returns NIL, but okay.

  > (chatty-progn (values 1 2 3))
  form (VALUES 1 2 3) returned (VALUES 1 2 3)
  1 ;
  2 ;
  3

  > (chatty-progn 1 2 (values 3))
  form 1 returned (VALUES 1)
  form 2 returned (VALUES 2)
  form (VALUES 3) returned (VALUES 3)
  3

  > (chatty-progn (print "foo") (print "bar"))

  "foo" form (PRINT "foo") returned (VALUES "foo")

  "bar" form (PRINT "bar") returned (VALUES "bar")
  "bar"


  > (catch 'it (chatty-progn 1 (values 2 3) (throw 'it 42) (values 4
5)))
  form 1 returned (VALUES 1)
  form (VALUES 2 3) returned (VALUES 2 3)
  form (THROW 'IT 42) jumped out
  42
From: Rob Warnock
Subject: Re: need help for a simple macro
Date: 
Message-ID: <u6WdnTUwjf0k_SPanZ2dnUVZ_tuonZ2d@speakeasy.net>
Kaz Kylheku  <········@gmail.com> wrote:
+---------------
| I'd give a different name to the macro version, like CHATTY-PROGN. The
| macro version should handle multiple values from each form, in
| particular the last one. I'd make it like this:
| 
|   (defmacro chatty-progn (&body forms)  ...[trimmed]... )
+---------------

Interesting that you added the multiple values support.
I've had a version of this in my toolbox for some years
*without* the multiple values support, and never missed it:

    (defmacro dbgv ((&optional (where "Some Unknown Location...")) &rest forms)
      `(progn
	 (format t "~&DBGV: @~a:~%" ',where)
	 ,@(loop for form in forms
	     collect `(format t "~s = ~s~%" ',form ,form))))

used like this:

    > (let ((x 3) (y 17))
	(dbgv (let-demo)
	  x
	  y
	  (1+ x)
	  (* 3 (- y 9))
	  (floor y x)))
    DBGV: @LET-DEMO:
    X = 3
    Y = 17
    (1+ X) = 4
    (* 3 (- Y 9)) = 24
    (FLOOR Y X) = 5
    NIL
    > (floor 17 3)

    5
    2
    > 

Oops. Now I have to figure out how to cleanly integrate multiple
values without cluttering up the simple case... (*grumph!*)  ;-}


-Rob

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