Definir una macro que genere una macro que acepte argumentos.

Definir una macro que genere una macro que acepte argumentos.

He usado látex desde hace bastante tiempo, pero solo me limito a lo básico. Intento utilizar paquetes siempre que sea posible. Ahora estoy intentando escribir algunos códigos yo mismo para que mi documento de látex sea más limpio. Esto es lo que quiero lograr:

Cuando defino:

\symb{flow}{f_#1^#2}[a,b]

quiero poder usar

\flow        % outputs $f_a^b$
\flow[x,y]   % outputs $f_x^y$

Tenga en cuenta que el número de índices no debe ser necesariamente 2, podría ser 1 o podría ser más de 2.

Lo siguiente es lo que tengo ahora:

\NewDocumentCommand{\symb}{m m m}
{ \expandafter\NewDocumentCommand\csname#1\endcsname{>{\SplitList{,}}O{#3}}
    { 
        % Not sure what I need to write here
    }
}

Básicamente, quiero poder usarlo \symb{flow}{f_#1^#2}[a,b]para definir una macro \flowque acepte un argumento opcional, que son índices de la variable delimitados por comas. En el caso de que no se proporcione el argumento opcional, se utilizarán los índices predeterminados (en este caso a, b).

En Python, esto se escribiría como:

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$

Respuesta1

Puedes hacerlo, pero no debes tomar Python como modelo para programar en LaTeX.

En LaTeX, los argumentos están entre llaves y no pueden sustituirse por listas de comas.

De todos modos, aquí hay una implementación con cualquier número de argumentos en la plantilla (pero, por supuesto, debes especificar cuántos quieres).

\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}

ingrese la descripción de la imagen aquí

Algunas palabras más sobre el código. Primero las cosas fáciles:

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

es la expl3versión de

\expandafter\NewDocumentCommand\csname#1\endcsname { O{#4 } }

y debería ser preferido.

A continuación una descripción de cómo \symbfunciona. Primero define una función interna con tantos argumentos como declarados; su texto de reemplazo lo proporciona la plantilla proporcionada. Así \symb{flow}{2}{f_{#1}^{#2}}{a,b}define

\kelvin_symb_flow:nn { f_{#1}^{#2} }

Nota. No hay problema con el hecho de que _sea una carta en el alcance de \ExplSyntaxOn, porque la declaración \symb{flow}{2}{...}{...}se daafueraese alcance.

Después de eso, definimos el comando de nivel de usuario \flowcon un argumento opcional, cuyo valor predeterminado es el cuarto argumento \symb. Este comando llama indirectamente a la función previamente definida mediante \__kelvin_symb_do:nnx.

El primer argumento será, en este caso, flowel segundo es el número de argumentos; su propósito es poder llamar a la función interna. El último argumento es la lista de comas (la predeterminada o la que se proporciona como argumento opcional para \flow) pero preprocesada para que genere una lista de elementos entre llaves.

La versión normal \__kelvin_symb_do:nnnsimplemente forma la función interna \kelvin_symb_flow:nny abre el tercer argumento. Pero estamos usando una variante del mismo; cuando

\clist_map_function:nN { #1 } \__kelvin_symb_brace:n

está completamente expandido (debido a la xvariante), si se a,ble aplica produce {a}{b}. Así terminamos con

\kelvin_symb_flow:nn { a } { b }

y LaTeX se complace en expandirse, como siempre, a f_{a}^{b}.

Respuesta2

Por ejemplo, puedes usar esto:

\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]$.

La segunda versión de esta macro implementa sus requisitos de los comentarios:

\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]

Esta solución difiere de la solución de egreg: no necesita explicar macros complicadas, sólo se utilizan primitivas TeX.

Respuesta3

Esto es similar al de Wipet pero hace que el argumento symb sea obligatorio como se explica en los comentarios.

ingrese la descripción de la imagen aquí

\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}

Y una versión que permite múltiples (hasta 8) entradas en la lista de argumentos.

ingrese la descripción de la imagen aquí

\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}

información relacionada