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.
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
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.
> 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
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/
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
> 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.
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
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
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.
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
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.
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
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
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
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
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