I suggest you look up "memo functions" and see if that technology
would help you.
Hello,
finally I felt the need to write my first macro. I have read Practical
Common Lisp's chapters 7 and 8 and made my way up to half of chapter 7
of "On Lisp", sometimes think I got it, but when trying a write it down
I struggle a lot, so Perlism 116 applies:
http://www.cs.yale.edu/homes/perlis-alan/quotes.html
(-:
Here is what I am trying (all simplified): I have an expensive function,
say
(defun expensive-defun (list)
(sleep 3)
(mapcar #'1+ list))
that is called over and over again by
(defun frequently-run-defun (number)
(case number
(1 (expensive-defun (list 0 1 2)))
(2 (expensive-defun (list 1 2 3)))
(3 (expensive-defun (list 2 3 4)))))
In fact, it would be possible and of course faster at runtime to
calculate the results in advance, such as
(defun frequently-run-defun-fast (number)
(case number
(1 '(1 2 3))
(2 '(2 3 4))
(3 '(3 4 5))))
But the original representation as (list 0 1 2), ... would feel much
more natural (in the sense of domain-specific thinking) in the source
code and so I had the idea to write a macro that transforms
(cheap-macro (list 0 1 2))
or
(cheap-macro '(0 1 2))
into
'(1 2 3)
and then have
(defun frequently-run-defun-macro (number)
(case number
(1 (cheap-macro (list 0 1 2)))
(2 (cheap-macro (list 1 2 3)))
(3 (cheap-macro (list 2 3 4)))))
which gives me more intuitively readable AND faster running code,
so the best of both worlds.
I tried (more poking around in the dark) various combinations of quotes, backquotes and commas, but failed miserably. What did work was
(defmacro cheap-macro (list)
`(quote ,(eval `(mapcar #'1+ ,list))))
but I cannot belief that I will have to fall back to eval for such a
trivial macro. It is much more likely that I am still lacking
understanding.
What would be the "best-practice" way to implement this macro?
Jeff Barnett <jbb@notatt.com> writes:
I suggest you look up "memo functions" and see if that technology
would help you.
Are you referring to "memoization"? I am aware of the concept, but
dismissed it so far, because I am using the lists resulting from the
"case" form for some lookup with a more or less random number, so not repeating often. Roughly like
(position-if #'(lambda (elt) (> elt (random 1.0)))
resulting-list-from-case-form)
However, I might put this technique to good use for the "case" form
itself. Thanks for the idea, which is somewhat orthogonal to my original question.
Here is what I am trying (all simplified): I have an expensive function,
say
(defun expensive-defun (list)
(sleep 3)
(mapcar #'1+ list))
that is called over and over again by
(defun frequently-run-defun (number)
(case number
(1 (expensive-defun (list 0 1 2)))
(2 (expensive-defun (list 1 2 3)))
(3 (expensive-defun (list 2 3 4)))))
In fact, it would be possible and of course faster at runtime to
calculate the results in advance, such as
(defun frequently-run-defun-fast (number)
(case number
(1 '(1 2 3))
(2 '(2 3 4))
(3 '(3 4 5))))
But the original representation as (list 0 1 2), ... would feel much
more natural (in the sense of domain-specific thinking) in the source
code and so I had the idea to write a macro that transforms
(cheap-macro (list 0 1 2))
or
(cheap-macro '(0 1 2))
into
'(1 2 3)
and then have
(defun frequently-run-defun-macro (number)
(case number
(1 (cheap-macro (list 0 1 2)))
(2 (cheap-macro (list 1 2 3)))
(3 (cheap-macro (list 2 3 4)))))
which gives me more intuitively readable AND faster running code,
so the best of both worlds.
I tried (more poking around in the dark) various combinations of quotes, backquotes and commas, but failed miserably. What did work was
(defmacro cheap-macro (list)
`(quote ,(eval `(mapcar #'1+ ,list))))
but I cannot belief that I will have to fall back to eval for such a
trivial macro. It is much more likely that I am still lacking
understanding.
What would be the "best-practice" way to implement this macro?
What would be the "best-practice" way to implement this macro?
(defun frequently-run-defun (number)
(case number
(1 (load-time-value (expensive-defun (list 0 1 2))))
(2 (load-time-value (expensive-defun (list 1 2 3))))
(3 (load-time-value (expensive-defun (list 2 3 4))))))
In TeX, one can use \expandafter to add another level of makro
expansion to a function definition.
(1 (expensive-defun (list 0 1 2)))
\x=macro:l.4 \show\x
\a .
\y=macro:l.5 \show\y
abc.
One way would be:
(defun frequently-run-defun (number)
(case number
(1 (load-time-value (expensive-defun (list 0 1 2))))
(2 (load-time-value (expensive-defun (list 1 2 3))))
(3 (load-time-value (expensive-defun (list 2 3 4))))))
When this code is compiled, the compiler arranges for the expensive calculations to be run one time when the compiled code is loaded.
The load-time values then become de facto literal objects that
are just retrieved whenever the expression's value is needed.
When you need unusual evaluation, then unless there is an existing
gadget for doing that (e.g. load-time-value if you want load-time evaluation), then you will end up coding an eval somewhere.
You just don't see explicit uses of the eval functions if you stick
to invoking evaluation in all the situations that the language
designers designed for you.
"evil eval" really just refers to going off the beaten path
there is no funtion read-contents-of-file-as-string, but you can
easily write one.
If on the other hand you only want cheap-macro to work with a list
argument and all the elements of the list will be constant numbers
then writing (cheap-macro (list 0 1 2)) or (cheap-macro '(0 1 2)) is redundant , all you really need is (cheap-macro (0 1 2)) .
By the way , expensive-defun suggests to me that executing the DEFUN
form will be expensive whereas what you really mean is that the
function will be expensive. So expensive-function is a more suggestive
name IMO.
Spiros Bousbouras <spibou@gmail.com> writes:
If on the other hand you only want cheap-macro to work with a list
argument and all the elements of the list will be constant numbers
Yes.
then writing (cheap-macro (list 0 1 2)) or (cheap-macro '(0 1 2)) is
redundant , all you really need is (cheap-macro (0 1 2)) .
That was the point I was crucially missing, you made my day, many
thanks!
Because of this misconception one of my tries, the rather "canonical"
(defmacro cheap-macro (list)
`(mapcar #'1+ ,list))
was dismissed by me to early: Since, say,
(cheap-macro (list 1 2 3))
evaluated to
(2 3 4)
I thought this to be not enough and tried hard to somehow put a quote in front of this. Had I tried this macro "downstream" I would have seen
that it does the job.
Axel Reichert <mail@axel-reichert.de> writes:
(1 (expensive-defun (list 0 1 2)))
In TeX, one can use \expandafter to add another
level of makro expansion to a function definition.
\def\a{abc}
\def\x{\a}
\expandafter\def\expandafter\y\expandafter{\a}
\show\x
\show\y
\end
It's like \expandafter is a control sequence that has a state
machine hidden behind it?
\x=macro:l.5 \show\x
\a .
\y=macro:l.6 \show\y
abc.
\z=macro:l.7 \show\z
\a .
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 293 |
Nodes: | 16 (2 / 14) |
Uptime: | 240:40:01 |
Calls: | 6,624 |
Files: | 12,173 |
Messages: | 5,320,078 |