hi i have this function i wrote for a lisp which
uses dynamic scoping.
QUESTION: How would i convert the
function to use lexical scope? Can it be
done with a macro? Or is there some other trick
that i should be using?
The function works like this e.g.,
(vsetq (a (b c) d) (list 1 (list 2 3) 4))
This sets a to 1, b to 2, c to 3, and d to 4.
The function assumes the structure of the variables
list (first argument) matches the structure
of the values list (second argument).
I can also put nil anyhere in the variables list
to mean, do not set any variable for this position.
(vsetq (a (nil c) d) (list 1 (list 2 3) 4))
this only sets a, c, and d.
I know at compile time what the variable list is,
but i do not know until runtime what the values list is.
E.g., i have a function which returns a bounding box
of a polygon. It returns a list of two sublists, each
sublist contains an x and y coordinate (both numbers).
I can get their values easily into variables as follows.
(vsetq (( left bottom) (right top)) (getBbox obj))
Here is the code using dynamic scope:
(defun vset ( vars vals)
(cond
((null vars) nil)
((atom vars)
(set vars vals))
(t
(vsetq (car vars) (car vals))
(vsetq (cdr vars) (cdr vals)))))
(defmacro vsetq ( vars vals)
`(vset ',vars ,vals))
Jim Newton wrote:
> hi i have this function i wrote for a lisp which
> uses dynamic scoping.
>
> QUESTION: How would i convert the
> function to use lexical scope? Can it be
> done with a macro? Or is there some other trick
> that i should be using?
>
> The function works like this e.g.,
>
> (vsetq (a (b c) d) (list 1 (list 2 3) 4))
>
> This sets a to 1, b to 2, c to 3, and d to 4.
> The function assumes the structure of the variables
> list (first argument) matches the structure
> of the values list (second argument).
> I can also put nil anyhere in the variables list
> to mean, do not set any variable for this position.
Is there a reason you're not using destructuring-bind?
If you really want to do this yourself, to get lexical scope, you're going
to have to use a macro. I would write a macro that generates an appropriate
LET or LET* form.
Chris Capel
In SKILL there is a function theEnvironment
which returns the current environment object.
such an object can be passed to the set function,
as well as other functions such as eval.
This can be done as follows:
(defmacro vsetq ( vars vals)
`(vset ',vars ,vals (theEnvironment)))
(defun vset ( vars vals env)
(cond
((null vars) nil)
((atom vars)
;; set the variable in the given environment
(set vars vals env))
(t
(vset (car vars) (car vals) env)
(vset (cdr vars) (cdr vals) env))))
I have seen that common lisp has environment
objects, but I have not yet figured out how
to get the current environment object nor
how to do setting and evaluating using a given
environment. Is it possible?
-jim
Jim Newton wrote:
> sorry, a little error in the vset function,
> it should call vset, not vsetq.
>
>
>
>> (defun vset ( vars vals)
>> (cond
>> ((null vars) nil)
>> ((atom vars)
>> (set vars vals))
>> (t
>> (vset (car vars) (car vals))
>> (vset (cdr vars) (cdr vals)))))
>>
>> (defmacro vsetq ( vars vals)
>> `(vset ',vars ,vals))
>>
>
Jim Newton wrote:
> I have seen that common lisp has environment
> objects, but I have not yet figured out how
> to get the current environment object nor
> how to do setting and evaluating using a given
> environment. Is it possible?
It's possible to get the current environment object via the &environment
parameter in a defmacro form. However, such an environment object is
only guaranteed to live during macro expansion. It's
implementation-dependent whether you can still use it at runtime. It's
probably not a good idea to do this anyway.
If you really need to have explicit access to variables at runtime, it's
easy to create your closures for doing this:
(lambda (var)
(cond
((eq var 'x) x)
((eq var 'y) y)
((eq var 'z) z)))
or shorter
(lambda (var)
(ecase var
(x x)
(y y)
(z z)))
You can easily build your own macro for doing this, for example
(capture-env x y z) could return such a closure.
If Common Lisp would allow to arbitrarily access variables from any
environment, it would be nearly impossible to get good optimization out
of compiled code. That's why the ANSI Common Lisp leaves it up to the
implementation whether it wants to provide this or not.
If the above doesn't help you, it may be a better idea to tell us what
the actual overall goal is. Maybe there are better ways to deal with the
problem. (For example, create hashtables, alists, or even objects for
grouping the stuff that you need?!?)
Pascal
--
The big bang way only works for god, everybody else has to use
evolution. - David Moon
Jim Newton <·····@rdrop.com> writes:
> hi i have this function i wrote for a lisp which
> uses dynamic scoping.
>
> QUESTION: How would i convert the
> function to use lexical scope? Can it be
> done with a macro? Or is there some other trick
> that i should be using?
Two cases:
- either the function makes effective use of the dynamic scoping, on
purpose, in which case you can translate it adding declarations to
get the dynamic scoping in Common Lisp: (DECLARE (SPECIAL dynvar))
- or the function does not really use dynamically scoping, but just
plain normal local variables that may happen to shadow unfortunately
dynamic variables, but without consequence, in which case you just
leave it alone, and get the correct lexical scoping at least.
So, if you're optimist, you just run it as is without change and see
if it still works. If you want a more controlled process, you could
start with a census of all global variables and search them in the
source to see if they're bound locally for their dynamic effect (ie,
if they're further used in called functions) or if they're only used
lexically as local variables.
> The function works like this e.g.,
>
> (vsetq (a (b c) d) (list 1 (list 2 3) 4))
> (defmacro vsetq ( vars vals)
A function is a function, a macro is a macro, and they're not the same thing.
Check DESTRUCTURING-BIND
--
__Pascal Bourguignon__ http://www.informatimago.com/
Litter box not here.
You must have moved it again.
I'll poop in the sink.
Jim Newton <·····@rdrop.com> writes:
> E.g., i have a function which returns a bounding box
> of a polygon. It returns a list of two sublists, each
> sublist contains an x and y coordinate (both numbers).
> I can get their values easily into variables as follows.
>
> (vsetq (( left bottom) (right top)) (getBbox obj))
This looks like the kind of thing that is most useful in a lisp that
doesn't have multiple return values. I.e., I probably would have made
a get-bbox function that returns four values, which would then let you
easily use multiple-value-setq.
That said, I this looks like a variation on a primitive
destructuring-bind. So, why not go full-on, and make a
destructuring-setq. You'd want to parse the lambda-list, extract the
variables, and expand to something like:
(destructuring-bind (#1=#:foo #2=#:bar (&key #3=#:baz)) (list 1 2 '(:baz 3))
(setq foo #1#
bar #2#
baz #3#))
As for directly translating your existing macro:
> Here is the code using dynamic scope:
>
>
> (defun vset ( vars vals)
> (cond
> ((null vars) nil)
> ((atom vars)
> (set vars vals))
> (t
> (vsetq (car vars) (car vals))
> (vsetq (cdr vars) (cdr vals)))))
This needs to be done at macro-expansion time. The ability to find a
variable from a (name, environment) tuple is compmiled away. So
instead of a function that recursively processes the list of vars and
calls SET, you'll want recursively create the macro expansion's SETQ
forms. The easiest approach might be to have an additional few
variables that act as cursors, so a call like this:
(vsetq (a (b . c) d) (get-values))
expands to:
(let (cursor1 cursor2)
(setq cursor1 (get-values)
a (car cursor1)
cursor1 (cdr cursor1)
cursor2 (car cursor1)
b (car cursor2)
c (cdr cursor2)
cursor1 (cdr cursor1)
d (car cursor1)))
Jim Newton <·····@rdrop.com> writes:
> (vsetq (a (b c) d) (list 1 (list 2 3) 4))
> This sets a to 1, b to 2, c to 3, and d to 4.
> I can also put nil anyhere in the variables list
> to mean, do not set any variable for this position.
> (vsetq (a (nil c) d) (list 1 (list 2 3) 4))
> this only sets a, c, and d.
Iterate's DSETQ maco does exactly this (including the NIL
feature). J. Amsterdam's Iterate package is older than the inclusion
of DESTRUCTURING-BIND into Common Lisp (and D-B does not provide the
"NIL to ignore" feature).
You may want to study its source code. Or just write a simple
list-transforming function to support the macro from scratch.
Regards,
Jorg Hohle
Telekom/T-Systems Technology Center