TeX - LaTeX Asked on June 9, 2021
While writing macros to manipulate the items of a list with a
@for
loop, I have found that braced items behave differently depending on whether they have a leading space or not:
documentclass{article}
usepackage[T1]{fontenc}
begin{document}
makeatletter
defuselist#1{%
@fortemp:=#1do{meaningtemppar}}
verb|listwithcommas{Lima,Alpha,{Delta,Oscar},Tango,{Whisky,Echo,Romeo},Xray}|
deflistwithcommas{Lima,Alpha,{Delta,Oscar},Tango,{Whisky,Echo,Romeo},Xray}
uselist{listwithcommas}
verb|listwithcommasandspaces{ {Lima}, Alpha, {Delta,Oscar}, Tango, {Whisky,Echo,Romeo}, Xray}|
deflistwithcommasandspaces{ {Lima}, Alpha, {Delta,Oscar}, Tango, {Whisky,Echo,Romeo}, Xray}
uselist{listwithcommasandspaces}
end{document}
I would like to maintain the braces, as when there is a space before the item, after applying the macros.
Is it possible to do that inside the @for
loop?
The intention is that, however the list is provided (through a macro), the output list will maintain the braces.
That is, both listwithcommas
and listwithcommasandspaces
should give the second output.
Another question is why the behaviour is different when there are spaces or not.
EDIT
Here is an example to clarify the intention.
Suppose I have a macro subtractlist
and that it is used two times.
First I subtract Lima
from the list and then {Delta,Oscar}
from the resulting list.
Since I have lost the braces after the first use, the second one will not work.
I can enclose all the items of the new formed list in braces.
In that case, it will work with listwithcommas
but not with listwithcommasandspaces
, because there are double braces and spaces.
documentclass{article}
usepackage[T1]{fontenc}
begin{document}
makeatletter
deflistwithcommas{{Lima},Alpha,{Delta,Oscar},Tango,{Whisky,Echo,Romeo},Xray}
deflistwithcommasandspaces{ {Lima}, Alpha, {Delta,Oscar}, Tango, {Whisky,Echo,Romeo}, Xray}
newifif@isinlist
defsubtractlist#1#2{% #1:original list, #2:remove list
gdef@subtractlist{}%
@for@tempa:=#1do{%
@isinlistfalse%
@for@tempb:=#2do{ifx@tempa@tempb@isinlisttruefi}%
if@isinlist%
else%
ifx@subtractlistempty%
expandaftergdef%
expandafter@subtractlist%
% expandafter{@tempa}%
%%% extra pair of braces added
expandafter{expandafter{@tempa}}%
else%
expandafterg@addto@macro%
expandafter@subtractlist%
% expandafter{expandafter,@tempa}%
%%% extra pair of braces added
expandafter{expandafter,expandafter{@tempa}}%
fi%
fi%
}%
letcurrentlist@subtractlist}
defremovelista{Bravo,Lima}
defremovelistb{Bravo,{Delta,Oscar}}
subtractlist{listwithcommas}{removelista}
verb|subtract Lima:| meaningcurrentlist
subtractlist{currentlist}{removelistb}
verb|subtract {Delta,Oscar}:|meaningcurrentlist
bigskip
subtractlist{listwithcommasandspaces}{removelista}
verb|subtract Lima:| meaningcurrentlist
subtractlist{currentlist}{removelistb}
verb|subtract {Delta,Oscar}:|meaningcurrentlist
end{document}
Without extra braces:
With extra braces:
@for
is really minimal, so it doesn't work too hard on removing or keeping braces and spaces. The braces are removed as part of TeX's argument grabbing, so you have to add extra safety to keep them. I added an @empty
in front of every item so that TeX won't remove braces, then I expand that @empty
to remove it before passing the item to uselistcmd
.
documentclass{article}
usepackage[T1]{fontenc}
begin{document}
makeatletter
defq@stop{q@stop}
defuselist#1{expandafteruselist@expandafter@empty#1,q@stop,}
defuselist@#1,{expandafteruselist@@expandafter{#1}}%
defuselist@@#1{%
ifxq@stop#1else
uselistcmd{#1}%
expandafteruselist@expandafter@empty
fi}
makeatother
defuselistcmd#1{detokenize{(#1)}par}
verb|listwithcommas{Lima,Alpha,{Delta,Oscar},Tango,{Whisky,Echo,Romeo},Xray}|
deflistwithcommas{Lima,Alpha,{Delta,Oscar},Tango,{Whisky,Echo,Romeo},Xray}
uselist{listwithcommas}
verb|listwithcommasandspaces{ {Lima}, Alpha, {Delta,Oscar}, Tango, {Whisky,Echo,Romeo}, Xray}|
deflistwithcommasandspaces{ {Lima}, Alpha, {Delta,Oscar}, Tango, {Whisky,Echo,Romeo}, Xray}
uselist{listwithcommasandspaces}
end{document}
Correct answer by Phelype Oleinik on June 9, 2021
If you look closely at your output, you'll see that the output includes a space for the value of temp
which is not the case with the first set.
What happens in the first case is that TeX is parsing everything up to the comma as the argument being digested. The braces get discarded here because it's getting passed a literal argument of, e.g., {Lima}
which it treats as a single argument and drops the braces, much like when you pass an argument to a macro enclosed in braces.
But in the space version, what's coming in is no longer {Lima}
but {Lima}
(the space doesn't get discarded because the expansion of @for
will be inserting non-letter tokens between @for
and the argument that gets digested. Now the argument is space plus a group so it keeps the braces because they're no longer at the outer level and discardable.
It's possible to do what you want (TeX is Turing-complete, after all), but it would not be simple. Enclosing every item in the list in braces and double-bracing the items you want to keep braces would work. Or you could add the spaces and parse them out of the items that you get. There might be some fancy stuff possible with token lists.
Answered by Don Hosek on June 9, 2021
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP