Graham's Ansi Common Lisp: p.170 having trouble understanding example
(defmacro random-choice (&rest exprs)
`(case (random ,(length exprs))
,@(let ((key -1))
(mapcar #'(lambda (expr)
`(,(incf key) ,expr))
exprs))))
So I performed macroexpand-1
on this function and I understand generally how this macro works, but I'm super confused of how Graham nest the backquote `, and how he uses ,@ to expand the cases.
- When can we nest backquotes?
- Why does Graham nest the backquotes in this example?
- Why does
,@
expand the the cases into the(random ,(length exprs))
cases? - I understand that the
mapcar
is primarily so that we can increase thekey
, but how does this macro know to applymapcar
a total of(random ,(length exprs))
times? - How is the implicit list that comma-at
,@
is splicing being formed?
Note I am very stupid so please explain in the most basic of terms.
EDIT:
I now understand that the inner most backquote (,(incf key) ,expr)
ensures that this function is evaluated first so its roughly equivalent to (list (incf key) expr)
, then
,@(let ((i 0))
(mapcar #'(lambda (expr)
`(,(incf i) ,expr))
args))
is evaluated into something like the list '((0 a_0) (1 a_1) ... (n a_n))
, and since we have ,@
then this is 'spliced' into
((0 a_0))
((1 a_n))
.
.
.
((n a_n))
Lastly (case (random ,(length exprs))
is evaluated to
case (random n)
and it gives us the outer parenthesis as well, leaving us with
(case (random n)
((0 a_0))
((1 a_n))
.
.
.
((n a_n)))
Did I get the sequence of events correct? I couldn't find any resources online to verify and Graham's book does not break it down like this.
lisp common-lisp
add a comment |
(defmacro random-choice (&rest exprs)
`(case (random ,(length exprs))
,@(let ((key -1))
(mapcar #'(lambda (expr)
`(,(incf key) ,expr))
exprs))))
So I performed macroexpand-1
on this function and I understand generally how this macro works, but I'm super confused of how Graham nest the backquote `, and how he uses ,@ to expand the cases.
- When can we nest backquotes?
- Why does Graham nest the backquotes in this example?
- Why does
,@
expand the the cases into the(random ,(length exprs))
cases? - I understand that the
mapcar
is primarily so that we can increase thekey
, but how does this macro know to applymapcar
a total of(random ,(length exprs))
times? - How is the implicit list that comma-at
,@
is splicing being formed?
Note I am very stupid so please explain in the most basic of terms.
EDIT:
I now understand that the inner most backquote (,(incf key) ,expr)
ensures that this function is evaluated first so its roughly equivalent to (list (incf key) expr)
, then
,@(let ((i 0))
(mapcar #'(lambda (expr)
`(,(incf i) ,expr))
args))
is evaluated into something like the list '((0 a_0) (1 a_1) ... (n a_n))
, and since we have ,@
then this is 'spliced' into
((0 a_0))
((1 a_n))
.
.
.
((n a_n))
Lastly (case (random ,(length exprs))
is evaluated to
case (random n)
and it gives us the outer parenthesis as well, leaving us with
(case (random n)
((0 a_0))
((1 a_n))
.
.
.
((n a_n)))
Did I get the sequence of events correct? I couldn't find any resources online to verify and Graham's book does not break it down like this.
lisp common-lisp
((0 a_0))
is not a validcase
clause. There are too many parentheses.
– Rainer Joswig
Nov 21 '18 at 14:37
1
@RainerJoswig As I read the spec,((0 a_0))
is a valid clause, it just doesn’t do what one expects. Specifically, it is the same as((0 a_0) (progn))
so is the clause meaning “if the thing is 0 ora_0
then evaluate to nil.” Although if all the clauses are in a case and of this form then the case won’t really do anything.
– Dan Robertson
Nov 21 '18 at 17:06
@DanRobertson: it will not do anything useful in the RANDOM-CHOICE case we are talking about, see the examples above - meaning it is not a valid clause for the purpose here. In general, you are right - the CASE clause is valid according to CL syntax.
– Rainer Joswig
Nov 21 '18 at 18:14
add a comment |
(defmacro random-choice (&rest exprs)
`(case (random ,(length exprs))
,@(let ((key -1))
(mapcar #'(lambda (expr)
`(,(incf key) ,expr))
exprs))))
So I performed macroexpand-1
on this function and I understand generally how this macro works, but I'm super confused of how Graham nest the backquote `, and how he uses ,@ to expand the cases.
- When can we nest backquotes?
- Why does Graham nest the backquotes in this example?
- Why does
,@
expand the the cases into the(random ,(length exprs))
cases? - I understand that the
mapcar
is primarily so that we can increase thekey
, but how does this macro know to applymapcar
a total of(random ,(length exprs))
times? - How is the implicit list that comma-at
,@
is splicing being formed?
Note I am very stupid so please explain in the most basic of terms.
EDIT:
I now understand that the inner most backquote (,(incf key) ,expr)
ensures that this function is evaluated first so its roughly equivalent to (list (incf key) expr)
, then
,@(let ((i 0))
(mapcar #'(lambda (expr)
`(,(incf i) ,expr))
args))
is evaluated into something like the list '((0 a_0) (1 a_1) ... (n a_n))
, and since we have ,@
then this is 'spliced' into
((0 a_0))
((1 a_n))
.
.
.
((n a_n))
Lastly (case (random ,(length exprs))
is evaluated to
case (random n)
and it gives us the outer parenthesis as well, leaving us with
(case (random n)
((0 a_0))
((1 a_n))
.
.
.
((n a_n)))
Did I get the sequence of events correct? I couldn't find any resources online to verify and Graham's book does not break it down like this.
lisp common-lisp
(defmacro random-choice (&rest exprs)
`(case (random ,(length exprs))
,@(let ((key -1))
(mapcar #'(lambda (expr)
`(,(incf key) ,expr))
exprs))))
So I performed macroexpand-1
on this function and I understand generally how this macro works, but I'm super confused of how Graham nest the backquote `, and how he uses ,@ to expand the cases.
- When can we nest backquotes?
- Why does Graham nest the backquotes in this example?
- Why does
,@
expand the the cases into the(random ,(length exprs))
cases? - I understand that the
mapcar
is primarily so that we can increase thekey
, but how does this macro know to applymapcar
a total of(random ,(length exprs))
times? - How is the implicit list that comma-at
,@
is splicing being formed?
Note I am very stupid so please explain in the most basic of terms.
EDIT:
I now understand that the inner most backquote (,(incf key) ,expr)
ensures that this function is evaluated first so its roughly equivalent to (list (incf key) expr)
, then
,@(let ((i 0))
(mapcar #'(lambda (expr)
`(,(incf i) ,expr))
args))
is evaluated into something like the list '((0 a_0) (1 a_1) ... (n a_n))
, and since we have ,@
then this is 'spliced' into
((0 a_0))
((1 a_n))
.
.
.
((n a_n))
Lastly (case (random ,(length exprs))
is evaluated to
case (random n)
and it gives us the outer parenthesis as well, leaving us with
(case (random n)
((0 a_0))
((1 a_n))
.
.
.
((n a_n)))
Did I get the sequence of events correct? I couldn't find any resources online to verify and Graham's book does not break it down like this.
lisp common-lisp
lisp common-lisp
edited Nov 21 '18 at 14:36
Rainer Joswig
110k8166283
110k8166283
asked Nov 21 '18 at 5:41
reposrepos
285
285
((0 a_0))
is not a validcase
clause. There are too many parentheses.
– Rainer Joswig
Nov 21 '18 at 14:37
1
@RainerJoswig As I read the spec,((0 a_0))
is a valid clause, it just doesn’t do what one expects. Specifically, it is the same as((0 a_0) (progn))
so is the clause meaning “if the thing is 0 ora_0
then evaluate to nil.” Although if all the clauses are in a case and of this form then the case won’t really do anything.
– Dan Robertson
Nov 21 '18 at 17:06
@DanRobertson: it will not do anything useful in the RANDOM-CHOICE case we are talking about, see the examples above - meaning it is not a valid clause for the purpose here. In general, you are right - the CASE clause is valid according to CL syntax.
– Rainer Joswig
Nov 21 '18 at 18:14
add a comment |
((0 a_0))
is not a validcase
clause. There are too many parentheses.
– Rainer Joswig
Nov 21 '18 at 14:37
1
@RainerJoswig As I read the spec,((0 a_0))
is a valid clause, it just doesn’t do what one expects. Specifically, it is the same as((0 a_0) (progn))
so is the clause meaning “if the thing is 0 ora_0
then evaluate to nil.” Although if all the clauses are in a case and of this form then the case won’t really do anything.
– Dan Robertson
Nov 21 '18 at 17:06
@DanRobertson: it will not do anything useful in the RANDOM-CHOICE case we are talking about, see the examples above - meaning it is not a valid clause for the purpose here. In general, you are right - the CASE clause is valid according to CL syntax.
– Rainer Joswig
Nov 21 '18 at 18:14
((0 a_0))
is not a valid case
clause. There are too many parentheses.– Rainer Joswig
Nov 21 '18 at 14:37
((0 a_0))
is not a valid case
clause. There are too many parentheses.– Rainer Joswig
Nov 21 '18 at 14:37
1
1
@RainerJoswig As I read the spec,
((0 a_0))
is a valid clause, it just doesn’t do what one expects. Specifically, it is the same as ((0 a_0) (progn))
so is the clause meaning “if the thing is 0 or a_0
then evaluate to nil.” Although if all the clauses are in a case and of this form then the case won’t really do anything.– Dan Robertson
Nov 21 '18 at 17:06
@RainerJoswig As I read the spec,
((0 a_0))
is a valid clause, it just doesn’t do what one expects. Specifically, it is the same as ((0 a_0) (progn))
so is the clause meaning “if the thing is 0 or a_0
then evaluate to nil.” Although if all the clauses are in a case and of this form then the case won’t really do anything.– Dan Robertson
Nov 21 '18 at 17:06
@DanRobertson: it will not do anything useful in the RANDOM-CHOICE case we are talking about, see the examples above - meaning it is not a valid clause for the purpose here. In general, you are right - the CASE clause is valid according to CL syntax.
– Rainer Joswig
Nov 21 '18 at 18:14
@DanRobertson: it will not do anything useful in the RANDOM-CHOICE case we are talking about, see the examples above - meaning it is not a valid clause for the purpose here. In general, you are right - the CASE clause is valid according to CL syntax.
– Rainer Joswig
Nov 21 '18 at 18:14
add a comment |
2 Answers
2
active
oldest
votes
When can we nest backquotes?
You can always nest backquotes. However note that this code does not nest them:
`(foo ; in 1 backquote here
,@(cons 'bar ; in 0 backquotes here
`(baz ; in 1 backquotes here
,(random 3))))
Nesting backquotes looks like:
`(let ((x `(,,y ,z))) ...)
Why does Graham nest the backquotes in this example?
He does not nest them. He generates the outer body of the case with the first backquotes, then fills it with the cases which are generated with mapcar
. To write the code to be generated for each case he uses a second backquote
Why does
,@
expand the the cases into the(random ,(length exprs))
cases?
It does not do that. It expands into (length exprs)
cases. Strictly it merges in the list of things returned by whatever is inside it, in this case a list of expressions.
I understand that the mapcar is primarily so that we can increase the key, but how does this macro know to apply mapcar a total of
(random ,(length exprs))
times?
That is not what the mapcar
does or what it is for.
How is the implicit list that comma-at ,@ is splicing being formed?
This is what mapcar does.
To address your edit, you got some things half correct but you have too many parens.
mapcar
applies a function to each element of a list in order, collecting the results into a list:
CL-USER> (mapcar #'1+ '(1 2 3))
(2 3 4)
CL-USER>(let ((key -1))
(mapcar (lambda (x)
`(,incf key) ,x))
'(foo bar (baz wazoo)))
((0 FOO) (1 BAR) (2 (BAZ WAZOO)))
This is going go into the body of the case
expression: If the random value is 0 then return FOO
, if it is 1 then BAR
, and so on.
To get this random value you do (random 3)
for a random integer between 0 and 2 inclusive.
Thanks! I have one more question, in the map car why is `(,(incf key) ,expr))
needed in evaluation but comma/backquote is not needed forexprs
. Ah! I think I answered it myself ...&rest
implicitly makesexprs
into a list. Thanks again.
– repos
Nov 21 '18 at 9:05
add a comment |
Another way to write it (getting rid of the LET, MAPCAR + side-effect INCF code):
CL-USER 44 > (defmacro random-choice (&rest exprs &aux (n (length exprs)))
`(case (random ,n)
,@(loop for ci below n and expr in exprs
collect `(,ci ,expr))))
RANDOM-CHOICE
CL-USER 45 > (macroexpand-1 '(random-choice 10 21 32 43))
(CASE (RANDOM 4) (0 10) (1 21) (2 32) (3 43))
The macro uses a backquote form with computation inside. We can extract the computation and assign the parts to variables:
CL-USER 46 > (defmacro random-choice (&rest exprs &aux (n (length exprs)))
(let ((keyform `(random ,n))
(clauses (loop for ci below n and expr in exprs
collect `(,ci ,expr))))
`(case ,keyform
,@clauses)))
RANDOM-CHOICE
CL-USER 47 > (macroexpand-1 '(random-choice 10 21 32 43))
(CASE (RANDOM 4) (0 10) (1 21) (2 32) (3 43))
As you can see the backquote forms can be computed independently and assembled in the backquote form at the end.
When the macro function contains code snippets, it is preferable to keep them as quoted or backquoted forms -> this makes them easier to identify in the macro function. Replacing them with list computation (using list
, cons
, ...) is less convenient and less readable. But one then needs to get the sequencing of backquote/unquote correct. In my example it's slightly easier, because the parts are computed independently. This helps to understand the macro, since it matches a bit more the syntax of case
:
CASE keyform {normal-clause}* [otherwise-clause]
normal-clause::= (keys form*)
Here we use only the keyform
and the 0..n-1 clauses from {normal-clause}*
. We also don't use the otherwise-clause
.
Hey Thanks! Your example made it more explicit of what the macro was doing.
– repos
Nov 23 '18 at 0:55
For the function header(defmacro random-choice (&rest exprs &aux (n (length exprs)))
is there no risk for variable capture for the variablen
? If so how do you incorporate agensym
in the function header? @RainerJoswig @rainer-joswig
– repos
Nov 23 '18 at 22:36
@repos: do you see a risk? Where? IsN
somewhere in the generated code?
– Rainer Joswig
Nov 23 '18 at 22:38
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53405865%2fgrahams-ansi-common-lisp-p-170-having-trouble-understanding-example%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
2 Answers
2
active
oldest
votes
2 Answers
2
active
oldest
votes
active
oldest
votes
active
oldest
votes
When can we nest backquotes?
You can always nest backquotes. However note that this code does not nest them:
`(foo ; in 1 backquote here
,@(cons 'bar ; in 0 backquotes here
`(baz ; in 1 backquotes here
,(random 3))))
Nesting backquotes looks like:
`(let ((x `(,,y ,z))) ...)
Why does Graham nest the backquotes in this example?
He does not nest them. He generates the outer body of the case with the first backquotes, then fills it with the cases which are generated with mapcar
. To write the code to be generated for each case he uses a second backquote
Why does
,@
expand the the cases into the(random ,(length exprs))
cases?
It does not do that. It expands into (length exprs)
cases. Strictly it merges in the list of things returned by whatever is inside it, in this case a list of expressions.
I understand that the mapcar is primarily so that we can increase the key, but how does this macro know to apply mapcar a total of
(random ,(length exprs))
times?
That is not what the mapcar
does or what it is for.
How is the implicit list that comma-at ,@ is splicing being formed?
This is what mapcar does.
To address your edit, you got some things half correct but you have too many parens.
mapcar
applies a function to each element of a list in order, collecting the results into a list:
CL-USER> (mapcar #'1+ '(1 2 3))
(2 3 4)
CL-USER>(let ((key -1))
(mapcar (lambda (x)
`(,incf key) ,x))
'(foo bar (baz wazoo)))
((0 FOO) (1 BAR) (2 (BAZ WAZOO)))
This is going go into the body of the case
expression: If the random value is 0 then return FOO
, if it is 1 then BAR
, and so on.
To get this random value you do (random 3)
for a random integer between 0 and 2 inclusive.
Thanks! I have one more question, in the map car why is `(,(incf key) ,expr))
needed in evaluation but comma/backquote is not needed forexprs
. Ah! I think I answered it myself ...&rest
implicitly makesexprs
into a list. Thanks again.
– repos
Nov 21 '18 at 9:05
add a comment |
When can we nest backquotes?
You can always nest backquotes. However note that this code does not nest them:
`(foo ; in 1 backquote here
,@(cons 'bar ; in 0 backquotes here
`(baz ; in 1 backquotes here
,(random 3))))
Nesting backquotes looks like:
`(let ((x `(,,y ,z))) ...)
Why does Graham nest the backquotes in this example?
He does not nest them. He generates the outer body of the case with the first backquotes, then fills it with the cases which are generated with mapcar
. To write the code to be generated for each case he uses a second backquote
Why does
,@
expand the the cases into the(random ,(length exprs))
cases?
It does not do that. It expands into (length exprs)
cases. Strictly it merges in the list of things returned by whatever is inside it, in this case a list of expressions.
I understand that the mapcar is primarily so that we can increase the key, but how does this macro know to apply mapcar a total of
(random ,(length exprs))
times?
That is not what the mapcar
does or what it is for.
How is the implicit list that comma-at ,@ is splicing being formed?
This is what mapcar does.
To address your edit, you got some things half correct but you have too many parens.
mapcar
applies a function to each element of a list in order, collecting the results into a list:
CL-USER> (mapcar #'1+ '(1 2 3))
(2 3 4)
CL-USER>(let ((key -1))
(mapcar (lambda (x)
`(,incf key) ,x))
'(foo bar (baz wazoo)))
((0 FOO) (1 BAR) (2 (BAZ WAZOO)))
This is going go into the body of the case
expression: If the random value is 0 then return FOO
, if it is 1 then BAR
, and so on.
To get this random value you do (random 3)
for a random integer between 0 and 2 inclusive.
Thanks! I have one more question, in the map car why is `(,(incf key) ,expr))
needed in evaluation but comma/backquote is not needed forexprs
. Ah! I think I answered it myself ...&rest
implicitly makesexprs
into a list. Thanks again.
– repos
Nov 21 '18 at 9:05
add a comment |
When can we nest backquotes?
You can always nest backquotes. However note that this code does not nest them:
`(foo ; in 1 backquote here
,@(cons 'bar ; in 0 backquotes here
`(baz ; in 1 backquotes here
,(random 3))))
Nesting backquotes looks like:
`(let ((x `(,,y ,z))) ...)
Why does Graham nest the backquotes in this example?
He does not nest them. He generates the outer body of the case with the first backquotes, then fills it with the cases which are generated with mapcar
. To write the code to be generated for each case he uses a second backquote
Why does
,@
expand the the cases into the(random ,(length exprs))
cases?
It does not do that. It expands into (length exprs)
cases. Strictly it merges in the list of things returned by whatever is inside it, in this case a list of expressions.
I understand that the mapcar is primarily so that we can increase the key, but how does this macro know to apply mapcar a total of
(random ,(length exprs))
times?
That is not what the mapcar
does or what it is for.
How is the implicit list that comma-at ,@ is splicing being formed?
This is what mapcar does.
To address your edit, you got some things half correct but you have too many parens.
mapcar
applies a function to each element of a list in order, collecting the results into a list:
CL-USER> (mapcar #'1+ '(1 2 3))
(2 3 4)
CL-USER>(let ((key -1))
(mapcar (lambda (x)
`(,incf key) ,x))
'(foo bar (baz wazoo)))
((0 FOO) (1 BAR) (2 (BAZ WAZOO)))
This is going go into the body of the case
expression: If the random value is 0 then return FOO
, if it is 1 then BAR
, and so on.
To get this random value you do (random 3)
for a random integer between 0 and 2 inclusive.
When can we nest backquotes?
You can always nest backquotes. However note that this code does not nest them:
`(foo ; in 1 backquote here
,@(cons 'bar ; in 0 backquotes here
`(baz ; in 1 backquotes here
,(random 3))))
Nesting backquotes looks like:
`(let ((x `(,,y ,z))) ...)
Why does Graham nest the backquotes in this example?
He does not nest them. He generates the outer body of the case with the first backquotes, then fills it with the cases which are generated with mapcar
. To write the code to be generated for each case he uses a second backquote
Why does
,@
expand the the cases into the(random ,(length exprs))
cases?
It does not do that. It expands into (length exprs)
cases. Strictly it merges in the list of things returned by whatever is inside it, in this case a list of expressions.
I understand that the mapcar is primarily so that we can increase the key, but how does this macro know to apply mapcar a total of
(random ,(length exprs))
times?
That is not what the mapcar
does or what it is for.
How is the implicit list that comma-at ,@ is splicing being formed?
This is what mapcar does.
To address your edit, you got some things half correct but you have too many parens.
mapcar
applies a function to each element of a list in order, collecting the results into a list:
CL-USER> (mapcar #'1+ '(1 2 3))
(2 3 4)
CL-USER>(let ((key -1))
(mapcar (lambda (x)
`(,incf key) ,x))
'(foo bar (baz wazoo)))
((0 FOO) (1 BAR) (2 (BAZ WAZOO)))
This is going go into the body of the case
expression: If the random value is 0 then return FOO
, if it is 1 then BAR
, and so on.
To get this random value you do (random 3)
for a random integer between 0 and 2 inclusive.
edited Nov 21 '18 at 9:11
Rainer Joswig
110k8166283
110k8166283
answered Nov 21 '18 at 8:25
Dan RobertsonDan Robertson
2,838612
2,838612
Thanks! I have one more question, in the map car why is `(,(incf key) ,expr))
needed in evaluation but comma/backquote is not needed forexprs
. Ah! I think I answered it myself ...&rest
implicitly makesexprs
into a list. Thanks again.
– repos
Nov 21 '18 at 9:05
add a comment |
Thanks! I have one more question, in the map car why is `(,(incf key) ,expr))
needed in evaluation but comma/backquote is not needed forexprs
. Ah! I think I answered it myself ...&rest
implicitly makesexprs
into a list. Thanks again.
– repos
Nov 21 '18 at 9:05
Thanks! I have one more question, in the map car why is `
(,(incf key) ,expr))
needed in evaluation but comma/backquote is not needed for exprs
. Ah! I think I answered it myself ...&rest
implicitly makes exprs
into a list. Thanks again.– repos
Nov 21 '18 at 9:05
Thanks! I have one more question, in the map car why is `
(,(incf key) ,expr))
needed in evaluation but comma/backquote is not needed for exprs
. Ah! I think I answered it myself ...&rest
implicitly makes exprs
into a list. Thanks again.– repos
Nov 21 '18 at 9:05
add a comment |
Another way to write it (getting rid of the LET, MAPCAR + side-effect INCF code):
CL-USER 44 > (defmacro random-choice (&rest exprs &aux (n (length exprs)))
`(case (random ,n)
,@(loop for ci below n and expr in exprs
collect `(,ci ,expr))))
RANDOM-CHOICE
CL-USER 45 > (macroexpand-1 '(random-choice 10 21 32 43))
(CASE (RANDOM 4) (0 10) (1 21) (2 32) (3 43))
The macro uses a backquote form with computation inside. We can extract the computation and assign the parts to variables:
CL-USER 46 > (defmacro random-choice (&rest exprs &aux (n (length exprs)))
(let ((keyform `(random ,n))
(clauses (loop for ci below n and expr in exprs
collect `(,ci ,expr))))
`(case ,keyform
,@clauses)))
RANDOM-CHOICE
CL-USER 47 > (macroexpand-1 '(random-choice 10 21 32 43))
(CASE (RANDOM 4) (0 10) (1 21) (2 32) (3 43))
As you can see the backquote forms can be computed independently and assembled in the backquote form at the end.
When the macro function contains code snippets, it is preferable to keep them as quoted or backquoted forms -> this makes them easier to identify in the macro function. Replacing them with list computation (using list
, cons
, ...) is less convenient and less readable. But one then needs to get the sequencing of backquote/unquote correct. In my example it's slightly easier, because the parts are computed independently. This helps to understand the macro, since it matches a bit more the syntax of case
:
CASE keyform {normal-clause}* [otherwise-clause]
normal-clause::= (keys form*)
Here we use only the keyform
and the 0..n-1 clauses from {normal-clause}*
. We also don't use the otherwise-clause
.
Hey Thanks! Your example made it more explicit of what the macro was doing.
– repos
Nov 23 '18 at 0:55
For the function header(defmacro random-choice (&rest exprs &aux (n (length exprs)))
is there no risk for variable capture for the variablen
? If so how do you incorporate agensym
in the function header? @RainerJoswig @rainer-joswig
– repos
Nov 23 '18 at 22:36
@repos: do you see a risk? Where? IsN
somewhere in the generated code?
– Rainer Joswig
Nov 23 '18 at 22:38
add a comment |
Another way to write it (getting rid of the LET, MAPCAR + side-effect INCF code):
CL-USER 44 > (defmacro random-choice (&rest exprs &aux (n (length exprs)))
`(case (random ,n)
,@(loop for ci below n and expr in exprs
collect `(,ci ,expr))))
RANDOM-CHOICE
CL-USER 45 > (macroexpand-1 '(random-choice 10 21 32 43))
(CASE (RANDOM 4) (0 10) (1 21) (2 32) (3 43))
The macro uses a backquote form with computation inside. We can extract the computation and assign the parts to variables:
CL-USER 46 > (defmacro random-choice (&rest exprs &aux (n (length exprs)))
(let ((keyform `(random ,n))
(clauses (loop for ci below n and expr in exprs
collect `(,ci ,expr))))
`(case ,keyform
,@clauses)))
RANDOM-CHOICE
CL-USER 47 > (macroexpand-1 '(random-choice 10 21 32 43))
(CASE (RANDOM 4) (0 10) (1 21) (2 32) (3 43))
As you can see the backquote forms can be computed independently and assembled in the backquote form at the end.
When the macro function contains code snippets, it is preferable to keep them as quoted or backquoted forms -> this makes them easier to identify in the macro function. Replacing them with list computation (using list
, cons
, ...) is less convenient and less readable. But one then needs to get the sequencing of backquote/unquote correct. In my example it's slightly easier, because the parts are computed independently. This helps to understand the macro, since it matches a bit more the syntax of case
:
CASE keyform {normal-clause}* [otherwise-clause]
normal-clause::= (keys form*)
Here we use only the keyform
and the 0..n-1 clauses from {normal-clause}*
. We also don't use the otherwise-clause
.
Hey Thanks! Your example made it more explicit of what the macro was doing.
– repos
Nov 23 '18 at 0:55
For the function header(defmacro random-choice (&rest exprs &aux (n (length exprs)))
is there no risk for variable capture for the variablen
? If so how do you incorporate agensym
in the function header? @RainerJoswig @rainer-joswig
– repos
Nov 23 '18 at 22:36
@repos: do you see a risk? Where? IsN
somewhere in the generated code?
– Rainer Joswig
Nov 23 '18 at 22:38
add a comment |
Another way to write it (getting rid of the LET, MAPCAR + side-effect INCF code):
CL-USER 44 > (defmacro random-choice (&rest exprs &aux (n (length exprs)))
`(case (random ,n)
,@(loop for ci below n and expr in exprs
collect `(,ci ,expr))))
RANDOM-CHOICE
CL-USER 45 > (macroexpand-1 '(random-choice 10 21 32 43))
(CASE (RANDOM 4) (0 10) (1 21) (2 32) (3 43))
The macro uses a backquote form with computation inside. We can extract the computation and assign the parts to variables:
CL-USER 46 > (defmacro random-choice (&rest exprs &aux (n (length exprs)))
(let ((keyform `(random ,n))
(clauses (loop for ci below n and expr in exprs
collect `(,ci ,expr))))
`(case ,keyform
,@clauses)))
RANDOM-CHOICE
CL-USER 47 > (macroexpand-1 '(random-choice 10 21 32 43))
(CASE (RANDOM 4) (0 10) (1 21) (2 32) (3 43))
As you can see the backquote forms can be computed independently and assembled in the backquote form at the end.
When the macro function contains code snippets, it is preferable to keep them as quoted or backquoted forms -> this makes them easier to identify in the macro function. Replacing them with list computation (using list
, cons
, ...) is less convenient and less readable. But one then needs to get the sequencing of backquote/unquote correct. In my example it's slightly easier, because the parts are computed independently. This helps to understand the macro, since it matches a bit more the syntax of case
:
CASE keyform {normal-clause}* [otherwise-clause]
normal-clause::= (keys form*)
Here we use only the keyform
and the 0..n-1 clauses from {normal-clause}*
. We also don't use the otherwise-clause
.
Another way to write it (getting rid of the LET, MAPCAR + side-effect INCF code):
CL-USER 44 > (defmacro random-choice (&rest exprs &aux (n (length exprs)))
`(case (random ,n)
,@(loop for ci below n and expr in exprs
collect `(,ci ,expr))))
RANDOM-CHOICE
CL-USER 45 > (macroexpand-1 '(random-choice 10 21 32 43))
(CASE (RANDOM 4) (0 10) (1 21) (2 32) (3 43))
The macro uses a backquote form with computation inside. We can extract the computation and assign the parts to variables:
CL-USER 46 > (defmacro random-choice (&rest exprs &aux (n (length exprs)))
(let ((keyform `(random ,n))
(clauses (loop for ci below n and expr in exprs
collect `(,ci ,expr))))
`(case ,keyform
,@clauses)))
RANDOM-CHOICE
CL-USER 47 > (macroexpand-1 '(random-choice 10 21 32 43))
(CASE (RANDOM 4) (0 10) (1 21) (2 32) (3 43))
As you can see the backquote forms can be computed independently and assembled in the backquote form at the end.
When the macro function contains code snippets, it is preferable to keep them as quoted or backquoted forms -> this makes them easier to identify in the macro function. Replacing them with list computation (using list
, cons
, ...) is less convenient and less readable. But one then needs to get the sequencing of backquote/unquote correct. In my example it's slightly easier, because the parts are computed independently. This helps to understand the macro, since it matches a bit more the syntax of case
:
CASE keyform {normal-clause}* [otherwise-clause]
normal-clause::= (keys form*)
Here we use only the keyform
and the 0..n-1 clauses from {normal-clause}*
. We also don't use the otherwise-clause
.
edited Nov 21 '18 at 9:16
answered Nov 21 '18 at 9:10
Rainer JoswigRainer Joswig
110k8166283
110k8166283
Hey Thanks! Your example made it more explicit of what the macro was doing.
– repos
Nov 23 '18 at 0:55
For the function header(defmacro random-choice (&rest exprs &aux (n (length exprs)))
is there no risk for variable capture for the variablen
? If so how do you incorporate agensym
in the function header? @RainerJoswig @rainer-joswig
– repos
Nov 23 '18 at 22:36
@repos: do you see a risk? Where? IsN
somewhere in the generated code?
– Rainer Joswig
Nov 23 '18 at 22:38
add a comment |
Hey Thanks! Your example made it more explicit of what the macro was doing.
– repos
Nov 23 '18 at 0:55
For the function header(defmacro random-choice (&rest exprs &aux (n (length exprs)))
is there no risk for variable capture for the variablen
? If so how do you incorporate agensym
in the function header? @RainerJoswig @rainer-joswig
– repos
Nov 23 '18 at 22:36
@repos: do you see a risk? Where? IsN
somewhere in the generated code?
– Rainer Joswig
Nov 23 '18 at 22:38
Hey Thanks! Your example made it more explicit of what the macro was doing.
– repos
Nov 23 '18 at 0:55
Hey Thanks! Your example made it more explicit of what the macro was doing.
– repos
Nov 23 '18 at 0:55
For the function header
(defmacro random-choice (&rest exprs &aux (n (length exprs)))
is there no risk for variable capture for the variable n
? If so how do you incorporate a gensym
in the function header? @RainerJoswig @rainer-joswig– repos
Nov 23 '18 at 22:36
For the function header
(defmacro random-choice (&rest exprs &aux (n (length exprs)))
is there no risk for variable capture for the variable n
? If so how do you incorporate a gensym
in the function header? @RainerJoswig @rainer-joswig– repos
Nov 23 '18 at 22:36
@repos: do you see a risk? Where? Is
N
somewhere in the generated code?– Rainer Joswig
Nov 23 '18 at 22:38
@repos: do you see a risk? Where? Is
N
somewhere in the generated code?– Rainer Joswig
Nov 23 '18 at 22:38
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53405865%2fgrahams-ansi-common-lisp-p-170-having-trouble-understanding-example%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
((0 a_0))
is not a validcase
clause. There are too many parentheses.– Rainer Joswig
Nov 21 '18 at 14:37
1
@RainerJoswig As I read the spec,
((0 a_0))
is a valid clause, it just doesn’t do what one expects. Specifically, it is the same as((0 a_0) (progn))
so is the clause meaning “if the thing is 0 ora_0
then evaluate to nil.” Although if all the clauses are in a case and of this form then the case won’t really do anything.– Dan Robertson
Nov 21 '18 at 17:06
@DanRobertson: it will not do anything useful in the RANDOM-CHOICE case we are talking about, see the examples above - meaning it is not a valid clause for the purpose here. In general, you are right - the CASE clause is valid according to CL syntax.
– Rainer Joswig
Nov 21 '18 at 18:14