9

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}
{ \expandafter\NewDocumentCommand\csname#1\endcsname{>{\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$

2
  • why is the third option to \symb optional (and what is the default if not given, that is what do you want \symb{flow}{f_#1^#2} to define? Commented Sep 10, 2020 at 15:37
  • It should indeed be mandatory, and not optional.
    – Kelvin Lee
    Commented Sep 10, 2020 at 15:42

3 Answers 3

15

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}

enter image description here

Some more words on the code. First the easy things:

\exp_args:Nc \NewDocumentCommand { #1 } { O{#4} }

is the expl3 version of

\expandafter\NewDocumentCommand\csname#1\endcsname { 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}.

2
  • Thank you for your effort in going through the codes line by line, definitely does help me to understand the code a lot better! Appreciate that very much!
    – Kelvin Lee
    Commented Sep 11, 2020 at 1:28
  • 1
    Can we use Latex old version programming in new Latex (version 2020 and later)?
    – user108724
    Commented Sep 11, 2020 at 4:37
8

For example, you can use this:

\def\sdef#1{\expandafter\def\csname#1\endcsname}

\def\symb#1#2[#3,#4]{%
  \sdef{#1}{\expandafter\futurelet\expandafter\next\csname#1:a\endcsname}%
  \sdef{#1:a}{\ifx\next[\csname#1:b\expandafter\endcsname 
              \else     \csname#1:b\endcsname[#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:

\def\sdef#1{\expandafter\def\csname#1\endcsname}
\def\addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}

\def\symb#1#2[#3]{%
  \sdef{#1}{\expandafter\futurelet\expandafter\next\csname#1:a\endcsname}%
  \sdef{#1:a}{\ifx\next[\csname#1:b\expandafter\endcsname 
              \else     \csname#1:c\endcsname #3,,,,,,,,\end \fi}%
  \sdef{#1:b}[##1]{\def\paramslistA{#3,}\def\paramslistB{}\setparams ##1,\end,
     \csname#1:c\expandafter\endcsname \paramslistB,,,,,,,,\end}%
  \sdef{#1:c}##1,##2,##3,##4,##5,##6,##7,##8,##9\end{#2}%
}
\def\setparams #1,{\ifx\end#1%
      \expandafter\addto\expandafter\paramslistB\expandafter{\paramslistA}%
   \else \expandafter \setparamslist \paramslistA \end #1,%
   \expandafter\setparams\fi}
\def\setparamslist#1,#2\end#3,{\def\paramslistA{#2}\addto\paramslistB{#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.

2
  • 1
    Sorry, I didn't make it clear previously, but the number of indices is arbitrary. It could be 2, more than that, or less than that. How should I go about changing the codes to take arbitrary number of indices?
    – Kelvin Lee
    Commented Sep 10, 2020 at 16:19
  • 1
    @KelvinLee OK, I added new macros.
    – wipet
    Commented Sep 12, 2020 at 7:39
5

This is similar to wipet's but makes the argument to symb mandatory as discussed in comments

enter image description here

\documentclass{article}

\def\symb#1#2#3{%
\expandafter\def\csname x#1\endcsname##1##2{#2}%
\expandafter\newcommand\csname #1\endcsname[1][#3]{%
  \expandafter\splitcomma\csname x#1\endcsname ##1\relax}}

\def\splitcomma#1#2,#3\relax{#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.

enter image description here

\documentclass{article}

\def\symb#1#2#3{%
\expandafter\def\csname x#1\endcsname##1##2##3##4##5##6##7##8##9{#2}%
\expandafter\newcommand\csname #1\endcsname[1][#3]{%
  \expandafter\splitcomma\csname x#1\endcsname ##1,,,,,,,,,,\relax}}


\def\splitcomma#1#2,#3,#4,#5,#6,#7,#8,#9\relax{#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}
3
  • Sorry, I didn't make it clear previously, but the number of indices is arbitrary. It could be 2, more than that, or less than that. How should I go about changing the codes to take arbitrary number of indices?
    – Kelvin Lee
    Commented Sep 10, 2020 at 16:19
  • 1
    @KelvinLee I might post later, if you make it that bit more complicated you need to iterate through the list the balance tips back to being easier with expl3 :-) Commented Sep 10, 2020 at 16:31
  • @KelvinLee extended version added Commented Sep 12, 2020 at 8:48

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .