Graham's Ansi Common Lisp: p.170 having trouble understanding example












3















(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 the key, but how does this macro know to apply mapcar 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.










share|improve this question

























  • ((0 a_0)) is not a valid case 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 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
















3















(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 the key, but how does this macro know to apply mapcar 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.










share|improve this question

























  • ((0 a_0)) is not a valid case 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 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














3












3








3








(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 the key, but how does this macro know to apply mapcar 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.










share|improve this question
















(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 the key, but how does this macro know to apply mapcar 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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 21 '18 at 14:36









Rainer Joswig

112k8169287




112k8169287










asked Nov 21 '18 at 5:41









reposrepos

285




285













  • ((0 a_0)) is not a valid case 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 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



















  • ((0 a_0)) is not a valid case 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 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

















((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












2 Answers
2






active

oldest

votes


















4















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.






share|improve this answer


























  • 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





















7














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.






share|improve this answer


























  • 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













  • @repos: do you see a risk? Where? Is N somewhere in the generated code?

    – Rainer Joswig
    Nov 23 '18 at 22:38













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
});


}
});














draft saved

draft discarded


















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









4















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.






share|improve this answer


























  • 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


















4















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.






share|improve this answer


























  • 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
















4












4








4








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.






share|improve this answer
















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.







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 21 '18 at 9:11









Rainer Joswig

112k8169287




112k8169287










answered Nov 21 '18 at 8:25









Dan RobertsonDan Robertson

3,323716




3,323716













  • 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



















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















7














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.






share|improve this answer


























  • 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













  • @repos: do you see a risk? Where? Is N somewhere in the generated code?

    – Rainer Joswig
    Nov 23 '18 at 22:38


















7














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.






share|improve this answer


























  • 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













  • @repos: do you see a risk? Where? Is N somewhere in the generated code?

    – Rainer Joswig
    Nov 23 '18 at 22:38
















7












7








7







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.






share|improve this answer















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.







share|improve this answer














share|improve this answer



share|improve this answer








edited Nov 21 '18 at 9:16

























answered Nov 21 '18 at 9:10









Rainer JoswigRainer Joswig

112k8169287




112k8169287













  • 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













  • @repos: do you see a risk? Where? Is N 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













  • 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



















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




















draft saved

draft discarded




















































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.




draft saved


draft discarded














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





















































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







Popular posts from this blog

Guess what letter conforming each word

Port of Spain

Run scheduled task as local user group (not BUILTIN)