TeX - LaTeX Asked by Kelvin Lee on December 4, 2020
I’ve used latex for quite some time now, but only limited to the very basics. I try to use packages whenever possible. I’m now trying to write some codes myself, in order to make my latex doc cleaner. This is what I want to achieve:
When I define:
symb{flow}{f_#1^#2}[a,b]
I want to be able to use
flow % outputs $f_a^b$
flow[x,y] % outputs $f_x^y$
Note that the number of indices must not necessarily be 2, it could be 1, or it could be more than 2.
The following is what I have now:
NewDocumentCommand{symb}{m m m}
{ expandafterNewDocumentCommandcsname#1endcsname{>{SplitList{,}}O{#3}}
{
% Not sure what I need to write here
}
}
Basically I want to be able to use symb{flow}{f_#1^#2}[a,b]
to define a macro flow
that takes in an optional argument, which is a comma-delimited indices of the variable. In the case where the optional argument is not provided, the default indices (in this case a, b) will be used.
In python, this would be written as:
def symb(expr, default):
def fn(*args):
if len(args) == 0:
return expr % args
else:
return expr % default
return fn
flow = symb('$f_%s^%s$', (a, b))
flow() % outputs $f_a^b$
flow(x, y) % outputs $f_x^y$
You can do it, but you should not take Python as a model for programming in LaTeX.
In LaTeX arguments are braced and cannot be substituted by comma lists.
Anyway, here's an implementation with any number of arguments in the template (but you of course have to specify how many you want).
documentclass{article}
usepackage{amsmath}
usepackage{xparse}
ExplSyntaxOn
NewDocumentCommand{symb}{mmmm}
{% #1=name of command to define, #2=number of args, #3=template, #4=default
% define the internal version
cs_new_protected:cn { kelvin_symb_#1:prg_replicate:nn{#2}{n} } { #3 }
% define the external version
exp_args:Nc NewDocumentCommand { #1 } { O{#4} }
{
__kelvin_symb_do:nnx { #1 } { #2 } { clist_map_function:nN { ##1 } __kelvin_symb_brace:n }
}
}
cs_new:Nn __kelvin_symb_do:nnn
{
use:c { kelvin_symb_#1:prg_replicate:nn{#2}{n} } #3
}
cs_generate_variant:Nn __kelvin_symb_do:nnn { nnx }
cs_new:Nn __kelvin_symb_brace:n { {#1} }
ExplSyntaxOff
symb{flow}{2}{f_{#1}^{#2}}{a,b}
symb{foo}{4}{int_{#1}^{#2}#3,d#4}{0,1,x,x}
begin{document}
$flow$
$flow[x,y]$
$foo$
$foo[a,b,f(x),x]$
end{document}
Some more words on the code. First the easy things:
exp_args:Nc NewDocumentCommand { #1 } { O{#4} }
is the expl3
version of
expandafterNewDocumentCommandcsname#1endcsname { O{#4 } }
and should be preferred.
Next a description of how symb
works. First it defines an internal function with as many arguments as declared; its replacement text is provided by the given template. Thus symb{flow}{2}{f_{#1}^{#2}}{a,b}
defines
kelvin_symb_flow:nn { f_{#1}^{#2} }
Note. There is no problem with the fact that _
is a letter in the scope of ExplSyntaxOn
, because the declaration symb{flow}{2}{...}{...}
is given outside that scope.
After that we define the user level command flow
with an optional argument, whose default is the fourth argument to symb
. This command indirectly calls the previously defined function by means of __kelvin_symb_do:nnx
.
The first argument will be, in this case, flow
, the second one is the number of arguments; their purpose is to be able to call the inner function. The last argument is the comma list (the default or the one given as optional argument to flow
) but preprocessed so that it yields a list of braced items.
The normal version __kelvin_symb_do:nnn
just forms the internal function kelvin_symb_flow:nn
and unbraces the third argument. But we're using a variant thereof; when
clist_map_function:nN { #1 } __kelvin_symb_brace:n
is fully expanded (because of the x
variant), if applied to a,b
it yields {a}{b}
. Thus we end up with
kelvin_symb_flow:nn { a } { b }
and LaTeX is happy to expand as usual to f_{a}^{b}
.
Correct answer by egreg on December 4, 2020
For example, you can use this:
defsdef#1{expandafterdefcsname#1endcsname}
defsymb#1#2[#3,#4]{%
sdef{#1}{expandafterfutureletexpandafternextcsname#1:aendcsname}%
sdef{#1:a}{ifxnext[csname#1:bexpandafterendcsname
else csname#1:bendcsname[#3,#4]fi}%
sdef{#1:b}[##1,##2]{#2}%
}
symb{flow}{f_#1^#2}[a,b]
$flow$ and $flow[x,y]$.
Second version of this macro implements your requirement from comments:
defsdef#1{expandafterdefcsname#1endcsname}
defaddto#1#2{expandafterdefexpandafter#1expandafter{#1#2}}
defsymb#1#2[#3]{%
sdef{#1}{expandafterfutureletexpandafternextcsname#1:aendcsname}%
sdef{#1:a}{ifxnext[csname#1:bexpandafterendcsname
else csname#1:cendcsname #3,,,,,,,,end fi}%
sdef{#1:b}[##1]{defparamslistA{#3,}defparamslistB{}setparams ##1,end,
csname#1:cexpandafterendcsname paramslistB,,,,,,,,end}%
sdef{#1:c}##1,##2,##3,##4,##5,##6,##7,##8,##9end{#2}%
}
defsetparams #1,{ifxend#1%
expandafteraddtoexpandafterparamslistBexpandafter{paramslistA}%
else expandafter setparamslist paramslistA end #1,%
expandaftersetparamsfi}
defsetparamslist#1,#2end#3,{defparamslistA{#2}addtoparamslistB{#3,}}
symb{flow}{f_#1^#2}[a,b]
symb{test}{test: 1=#1, 2=#2, 3=#3, 4=#4}[a,b,c,d]
$flow$ and $flow[x,y]$.
test
test[mmm]
test[x,y,z]
This solution differs from egreg's solution: you need not expl3 cmplicated macros, only TeX primitives are used.
Answered by wipet on December 4, 2020
This is similar to wipet's but makes the argument to symb mandatory as discussed in comments
documentclass{article}
defsymb#1#2#3{%
expandafterdefcsname x#1endcsname##1##2{#2}%
expandafternewcommandcsname #1endcsname[1][#3]{%
expandaftersplitcommacsname x#1endcsname ##1relax}}
defsplitcomma#1#2,#3relax{#1{#2}{#3}}
begin{document}
symb{flow}{f_#1^#2}{a,b}
$flow$
$flow[x,y]$
end{document}
And a version that allows multiple (up to 8) entries in the argument list.
documentclass{article}
defsymb#1#2#3{%
expandafterdefcsname x#1endcsname##1##2##3##4##5##6##7##8##9{#2}%
expandafternewcommandcsname #1endcsname[1][#3]{%
expandaftersplitcommacsname x#1endcsname ##1,,,,,,,,,,relax}}
defsplitcomma#1#2,#3,#4,#5,#6,#7,#8,#9relax{#1{#2}{#3}{#4}{#5}{#6}{#7}{#8}{#9}relax}
begin{document}
symb{flow}{f_#1^#2}{a,b}
symb{flowb}{f_#1^#2g_#3^#4}{a,b,c,d}
$flow$
$flow[x,y]$
$flowb$
$flowb[x,y,w,z]$
end{document}
Answered by David Carlisle on December 4, 2020
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP