Re: nested backquoting (newbie) question in Clisp
- From: "Kaz Kylheku" <kkylheku@xxxxxxxxx>
- Date: 26 Feb 2007 23:52:15 -0800
On Feb 26, 10:16 am, "erdibalint" <erdibal...@xxxxxxxxx> wrote:
Ok, so I have a seemingly simple task to accomplish:
A macro has to be defined that takes a number n followed by one or
more expressions,
and returns the value of the nth expression, so:
Make that: returns the /value(s)/ of the nth expression!
What if N is out of range? Error signaled? NIL?
(let ((n 2))
(nth-expr n (/ 1 0) (+ 1 2) (/ 1 0)))
In Common Lisp, we already have:
(prog1 ...) returns the first
(prog2 ...) returns the second
(progn ...) returns the last
I'd call this PROGI (i-th expression). And of course it would be
pronounced like ``perogi''. :)
should return 3.
My attempt:
(defmacro nth-expr (n &body exprs)
(let ((g (gensym)))
`(let ((,g ,n))
(if (> ,g ,(length exprs))
nil
(elt ',exprs (1- ,g))))))
One possibility is to compile the form into a case.
(case n
(0 first-expr)
(1 second-expr)
...
(otherwise ...))
The nice thing about this is that the compiler can then perhaps apply
some optimizations that are specific to CASE.
(defmacro progi (i &rest exprs)
`(case ,i
,@(loop for expr in exprs
for i from 0
collect `(,i ,expr))
(otherwise nil)))
So in theory I
should only turn evaluation on inside the last expression in the
defmacro but that leads to other problems.
But in practice there is no such switch.
The problem is not that insufficient evaluation is applied, but rather
that ELT is a function and not a control construct. You are relying on
compiling your construct to ELT, but ELT cannot express it.
You cannot write the code by hand using ELT. If /you/ cannot write it
that way, the macro cannot either.
You turned the expressions into a big literal list. The value of N is
used to pull out the N-th element of the literal using ELT. That
element is a piece of source code, however, which requires evaluation.
There is no way to get that to happen without calling the evaluator,
or dynamically compiling it and calling the resulting function.
Of course I could always wrap an eval around that one, so the last line becomes (eval (elt
',exprs (1- ,g))))))) but that seems very unprofessional. But then how
do I do this?
The translation to CASE is one way.
Another way is to emit a whole bunch of comparisons. A simple linear
search for instance:
(cond
((eql 0 n) (first-expr ...))
((eql 1 n) (second-expr ...))
...
(t nil)) ;; or however you handle that
A binary search would be better. Suppose there are 8 cases. You'd emit
a tree of IF's along these lines.
(if (< 4 n)
(if (< 2 n)
...
...)
(if (< 7 n)
...
...)))
I'd much rather leave this type of work to the compiler writer dealing
with the CASE construct.
How about roll your own jump table? To do this, you turn each
expression into a function. The translation would look like this:
;; suppose there are 10 exprs
(let ((case-vec (vector (lambda () (first-expr))
(lambda () (second-expr))
...)))
(if (<= 0 n 9) ;; range check
(funcall (aref vector n))
nil))
Of course, the constant 9 would actually be derived from counting the
number of expressions. The macro can do that since it has the source
code to the expressions, and then just insert it as a constant into
the generated code. And would not evaluate the controlling expression
twice but get it into a gensym variable:
(defmacro progi (i &rest exprs)
(let ((vec (gensym "VEC"))
(ival (gensym "I")))
`(let ((,vec (vector ,@(loop for expr in exprs
collect `(lambda () ,expr))))
(,ival ,i))
(if (<= 0 ,ival ,(1- (length exprs)))
(funcall (aref ,vec ,ival))
nil))))
So here we are exploiting the lexical closure's ability to control
evaluation, rather than exploiting control constructs like IF, COND or
CASE. By wrapping a code in a closure, we control whether or not that
code is evaluated by calling the closure or not calling the closure.
Since closures are objects, we stick them into a table, and index
directly into it.
.
- References:
- nested backquoting (newbie) question in Clisp
- From: erdibalint
- Re: nested backquoting (newbie) question in Clisp
- From: Kaz Kylheku
- Re: nested backquoting (newbie) question in Clisp
- From: erdibalint
- nested backquoting (newbie) question in Clisp
- Prev by Date: Re: Lisp Syntax - functions versus data
- Next by Date: Re: nested backquoting (newbie) question in Clisp
- Previous by thread: Re: nested backquoting (newbie) question in Clisp
- Next by thread: Re: nested backquoting (newbie) question in Clisp
- Index(es):
Relevant Pages
|