¿Cómo definir una macro con múltiples parámetros opcionales?

¿Cómo definir una macro con múltiples parámetros opcionales?

Después de que tuve una macro que funcionó, intenté mejorarla haciendo que algunos parámetros fueran opcionales. Lamentablemente la macro ya no funciona. En lugar de eso, recibo errores que no entiendo, por ejemplo:

LaTeX Warning: Label `####5' multiply defined.
LaTeX Warning: Label `####5' multiply defined.
! LaTeX Error: \fLab undefined.
! Illegal parameter number in definition of \fLab.
! Illegal parameter number in definition of \reserved@a.
! LaTeX Error: \fCap undefined.

...etcétera. El último código que probé se veía así:

%% Graphics figure with caption and label
% [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
\newcommand{\figCapLab}[5][htbp]{%
\ifthenelse{\equal{#5}{}}%
{\renewcommand{\fLab}{}}%
{\renewcommand{\fLab}{\label{##5}}}%
\ifthenelse{\equal{#4}{}}%
{\renewcommand{\fCap}{\fLab}}%
{\renewcommand{\fCap}{\caption{\fLab{\small{}##4}}}}%
\begin{figure}[#1]%
\centering%
\begin{minipage}[t]{#2\textwidth}%
\includegraphics[width=\textwidth]{#3}% is width of surrounding minipage
\fCap%
\end{minipage}%
\end{figure}
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
\newcommand{\figCap}[4]{%
\ifthenelse{\equal{#1}{}}%
{\figCapLab{#2}{#3}{#4}{}}%
{\figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
\newcommand{\figLab}[4]{%
\ifthenelse{\equal{#1}{}}%
{\figCapLab{#2}{#3}{}{#4}}%
{\figCapLab[#1]{#2}{#3}{}{#4}}%
}

¿Quién puede explicar qué salió mal?

Las personas a las que les gusten los ejemplos completos deberían agregar este prólogo:

\documentclass[a4paper,twoside]{report}
\usepackage{german}
\usepackage[latin1]{inputenc}
\usepackage{a4}
\usepackage{amsmath}
\usepackage{url}
\usepackage{graphicx}
\usepackage{ifthen}

...y este epílogo:

\begin{document}
See \ref{foo}.
\figCapLab{0.9}{whatever.pdf}{Caption}{foo}
\end{document}

Respuesta1

Cuando obedezco sus vagas instrucciones para crear yo mismo un ejemplo compilable que exhiba el comportamiento erróneo

\documentclass[a4paper,twoside]{report}
\usepackage{german}
\usepackage[latin1]{inputenc}
\usepackage{a4}
\usepackage{amsmath}
\usepackage{url}
\usepackage{graphicx}
\usepackage{ifthen}

%% Graphics figure with caption and label
% [1:placement,] 2:relative width, 3:file name[, 4:caption[, 5:label]]
\newcommand{\figCapLab}[5][htbp]{%
\ifthenelse{\equal{#5}{}}%
{\renewcommand{\fLab}{}}%
{\renewcommand{\fLab}{\label{##5}}}%
\ifthenelse{\equal{#4}{}}%
{\renewcommand{\fCap}{\fLab}}%
{\renewcommand{\fCap}{\caption{\fLab{\small{}##4}}}}%
\begin{figure}[#1]%
\centering%
\begin{minipage}[t]{#2\textwidth}%
\includegraphics[width=\textwidth]{#3}% is width of surrounding minipage
\fCap%
\end{minipage}%
\end{figure}
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
\newcommand{\figCap}[4]{%
\ifthenelse{\equal{#1}{}}%
{\figCapLab{#2}{#3}{#4}{}}%
{\figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
\newcommand{\figLab}[4]{%
\ifthenelse{\equal{#1}{}}%
{\figCapLab{#2}{#3}{}{#4}}%
{\figCapLab[#1]{#2}{#3}{}{#4}}%
}

\begin{document}
See \ref{foo}.
\figCapLab{0.9}{whatever.pdf}{Caption}{foo}
\end{document}

, No recibo ninguno de los errores que usted describe, pero obtengo:

LaTeX Warning: Reference `foo' on page 1 undefined on input line 43.


! LaTeX Error: \fLab undefined.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
 ...                                              

l.44 \figCapLab{0.9}{whatever.pdf}{Caption}{foo}

? 
! Illegal parameter number in definition of \fLab.
<to be read again> 
                   5
l.44 \figCapLab{0.9}{whatever.pdf}{Caption}{foo}

? 

! LaTeX Error: \fCap undefined.

See the LaTeX manual or LaTeX Companion for explanation.
Type  H <return>  for immediate help.
 ...                                              

l.44 \figCapLab{0.9}{whatever.pdf}{Caption}{foo}

? 
! Illegal parameter number in definition of \fCap.
<to be read again> 
                   4
l.44 \figCapLab{0.9}{whatever.pdf}{Caption}{foo}

? 

LaTeX Warning: File `whatever.pdf' not found on input line 44.


! Package pdftex.def Error: File `whatever.pdf' not found: using draft setting.


See the pdftex.def package documentation for explanation.
Type  H <return>  for immediate help.
 ...                                              

l.44 \figCapLab{0.9}{whatever.pdf}{Caption}{foo}

? 
! Illegal parameter number in definition of \reserved@a.
<to be read again> 
                   4
l.44 \figCapLab{0.9}{whatever.pdf}{Caption}{foo}

? 
! Illegal parameter number in definition of \reserved@a.
<to be read again> 
                   5
l.44 \figCapLab{0.9}{whatever.pdf}{Caption}{foo}

? 
! You can't use `macro parameter character #' in restricted horizontal mode.
<argument> ...e : \ignorespaces \fLab {\small {}##
                                                  4}
l.44 \figCapLab{0.9}{whatever.pdf}{Caption}{foo}

? 
[1{/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map}] (./test.aux)

LaTeX Warning: There were undefined references.

Cuando en cambio hago:

\documentclass[a4paper,twoside]{report}
\usepackage{german}
\usepackage[latin1]{inputenc}
\usepackage{a4}
\usepackage{amsmath}
\usepackage{url}
\usepackage{graphicx}
\usepackage{ifthen}

% As later \renwecommand is done on these macros, 
% they should be defined: !!!!!!!!!!!!
\newcommand{\fLab}{}
\newcommand{\fCap}{}

%% Graphics figure with caption and label
%  #1: optional: placement
%  #2: non-optional: relative width
%  #3: non-optional: file name
%  #4: non-optional: in case not empty: caption
%  #5: non-optional: in case not empty: label
\newcommand{\figCapLab}[5][htbp]{%
\ifthenelse{\equal{#5}{}}%
{\renewcommand{\fLab}{}}%
{\renewcommand{\fLab}{\label{#5}}}% !!!!Don't double the hash!!!!
\ifthenelse{\equal{#4}{}}%
{\renewcommand{\fCap}{\fLab}}%
{\renewcommand{\fCap}{\caption{\fLab{\small{}#4}}}}% !!!!Don't double the hash!!!!
\begin{figure}[#1]%
\centering
\begin{minipage}[t]{#2\textwidth}%
\includegraphics[width=\textwidth]{#3}% is width of surrounding minipage
\fCap
\end{minipage}%
\end{figure}%%%%%%%
}
%% Graphics figure with caption
% [1:placement,] 2:relative width, 3:file name, 4:caption
\newcommand{\figCap}[4]{%
\ifthenelse{\equal{#1}{}}%
{\figCapLab{#2}{#3}{#4}{}}%
{\figCapLab[#1]{#2}{#3}{#4}{}}%
}
%% Graphics figure with label
% [1:placement,] 2:relative width, 3:file name, 4:label
\newcommand{\figLab}[4]{%
\ifthenelse{\equal{#1}{}}%
{\figCapLab{#2}{#3}{}{#4}}%
{\figCapLab[#1]{#2}{#3}{}{#4}}%
}

\begin{document}
See \ref{foo}.
\figCapLab{0.9}{example-grid-100x100pt.pdf}{Caption}{foo}
\end{document}

, entonces no recibo ningún error ni advertencia con la segunda compilación.


Por cierto 1:

no tengolo que sea.pdfen los árboles texmf de mi sistema.

Si desea proporcionar ejemplos que procesen imágenes, puede utilizar las imágenes listas para usar que le ofrecen los sistemas LaTeX modernos:

Por ejemplo, la documentación de Martin Scharrer.paquete MWEenumera todas las imágenes disponibles debido a ese paquete. Con las plataformas TeX actuales y las versiones actuales delPaquete MWE, las imágenes se pueden utilizar sin la necesidad de cargar el paquete porque están integradas en el árbol texmf y en la base de datos de nombres de archivo.

Por cierto 2:

En circunstancias normales, puede sangrar líneas de código fuente TeX para mejorar la legibilidad. ;-) Esto se debe a que cuando (La)TeX comienza a leer una línea de entrada, el estado del aparato de lectura es el estado N (nueva línea), mientras que en el estado N los caracteres del código de categoría 10 (espacio) no serán tokenizados. como fichas de espacio, pero no producirá ninguna ficha.

Por cierto 3:

Es una buena práctica proporcionar ejemplos que las personas puedan compilar tal como están para reproducir exactamente el comportamiento erróneo. Ser capaz de reproducir un comportamiento erróneo es importante para poder depurar el código que produce ese comportamiento erróneo.


Dr. Nicola TalbotCreando un ejemplo mínimo de LaTeXproporciona algunas pautas.

También hayCómo hacer un “ejemplo mínimo”de texfaq.org que también contiene algunos enlaces a consejos para hacer preguntas.

En¿Qué es un ejemplo de trabajo mínimo?Christian Faulhammer no utiliza el término "ejemplo mínimo", pero utiliza el término "ejemplo mínimo de trabajo" incluso para ejemplos que funcionan sólo en el sentido de que son suficientes para exhibir un comportamiento erróneo.

También te pueden interesar las respuestas a la pregunta.Me acaban de pedir que escriba un ejemplo mínimo, ¿qué es eso?

Respuesta2

El OP me pidió una pequeña explicación para esta respuesta, así que agregaré algo de texto aquí. El mecanismo estándar de LaTeX para argumentos opcionales permite un argumento opcional que viene antes de los argumentos obligatorios. Por lo tanto, para crear una sintaxis que proporcione un argumento opcional, 2 argumentos obligatorios y luego 2 argumentos opcionales, es necesario encadenar 3 macros sucesivamente, ya que se solicitan 3 argumentos opcionales.

La primera macro absorbe un argumento opcional más 2 obligatorios, y luego debe invocar una segunda macro, y aquí está la clave:como su acción final! La razón por la que la segunda macro invocación debe ser la acción final de la primera macro es que la segunda macro debe absorber un argumento opcional. Si hay tokens en la primera macro que siguen a la invocación de la segunda macro en la cadena, entonces esos otros tokens en la primera macro se absorberán como argumento, en lugar de lo que se pretende.

Asimismo, la segunda macro debe invocar a la tercera macro como acción final.

Ciertos aspectos destacados extravagantes:

  1. Si la macro sucesiva se va a llamar dentro de un \ifbloque, debe hacerlo \expandafterantes de la macro sucesiva, para que la macro sucesiva no intente absorber \elseo \fi.

  2. En este caso particular, el argumento opcional final, la etiqueta, solo se puede invocar si se ha especificado un título. Por lo tanto, la segunda macro de la cadena solo llamará a la tercera macro si se ha especificado un título. De lo contrario, truncará la secuencia.

El MWE:

\documentclass{article}
\usepackage{graphicx}
\newcommand\addtofigtoks[1]{\expandafter\figtoks\expandafter
  {\the\figtoks#1}}
\newtoks\figtoks
\newcommand\figCapLab[3][htbp]{%
  \figtoks{\begin{figure}[#1]}
  \addtofigtoks{\centering}
  \addtofigtoks{\includegraphics[width=#2\textwidth]{#3}}
  \optcap
}
\newcommand\optcap[1][\relax]{%
  \ifx\relax#1\relax
    \addtofigtoks{\end{figure}}
    \the\figtoks
  \else
    \addtofigtoks{\caption{#1}}%
    \expandafter\labelopt
  \fi
}
\newcommand\labelopt[1][\relax]{%
  \ifx\relax#1\relax\else\addtofigtoks{\label{#1}}\fi
  \addtofigtoks{\end{figure}}
  \the\figtoks
}
\begin{document}
\figCapLab{.2}{example-image-a}
\figCapLab{.2}{example-image-b}[My caption]
\figCapLab{.2}{example-image-c}[My caption][fg:label1]
\figCapLab[p]{.2}{example-image}[Other caption][fg:label2]

In figures \ref{fg:label1} and \ref{fg:label2}...
\end{document}

ingrese la descripción de la imagen aquí

Ack-shu-ally, cuanto más lo pienso, las fichas ni siquiera son necesarias:

\documentclass{article}
\usepackage{graphicx}
\newcommand\figCapLab[3][htbp]{%
  \begin{figure}[#1]
  \centering
  \includegraphics[width=#2\textwidth]{#3}
  \optcap
}
\newcommand\optcap[1][\relax]{%
  \ifx\relax#1\relax
    \end{figure}
  \else
    \caption{#1}%
    \expandafter\labelopt
  \fi
}
\newcommand\labelopt[1][\relax]{%
  \ifx\relax#1\relax\else\label{#1}\fi
  \end{figure}
}
\begin{document}
\figCapLab{.2}{example-image-a}
\figCapLab{.2}{example-image-b}[My caption]
\figCapLab{.2}{example-image-c}[My caption][fg:label1]
\figCapLab[p]{.2}{example-image}[Other caption][fg:label2]

In figures \ref{fg:label1} and \ref{fg:label2}...
\end{document}

Respuesta3

Así es como puedes lograr tu objetivo usandoxparse:

ingrese la descripción de la imagen aquí

\documentclass{article}

\usepackage{graphicx,xparse}

% \figCapLab
%   [<float spec>]   #1
%   {<width factor>} #2
%   {<image>}        #3
%   [<caption>]      #4
%   [<label>]        #5
\NewDocumentCommand{\figCapLab}{ O{htbp} m m o o }{%
  \begin{figure}[#1]
    \centering
    \includegraphics[width=#2\linewidth]{#3}% Set image at width
    \IfValueT{#4}
      {\caption{#4}\IfValueT{#5}{\label{#5}}}% Set possible \caption and \label
  \end{figure}
}

\begin{document}

\figCapLab{.2}{example-image-a}
\figCapLab{.2}{example-image-b}[My caption]
\figCapLab{.2}{example-image-c}[My caption][fg:label1]
\figCapLab[p]{.2}{example-image}[Other caption][fg:label2]

In figures \ref{fg:label1} and \ref{fg:label2}\ldots

\end{document}

Los argumentos opcionales con un valor predeterminado se especifican usando, O{<default>}mientras que los argumentos opcionales sin un valor predeterminado se usan o. El condicionamiento sobre si se proporciona o no un valor se realiza mediante \IfValueTF{<parameter>}{<true>}{<false>}. También hay condicionales singulares \IfValueTy \IfValueF, el primero de los cuales se usó anteriormente.

El código anterior supone que un título vacío (cuarto argumento en blanco) no necesitaría un \label(quinto) argumento. Si es necesario, mueva la \IfValueT{#5}{\label{#5}}rama hacia <true>adentro \IfValueT{#4}:

\NewDocumentCommand{\figCapLab}{ O{htbp} m m o o }{%
  \begin{figure}[#1]
    \centering
    \includegraphics[width=#2\textwidth]{#3}% Set image at width
    \IfValueT{#4}{\caption{#4}}% Possible \caption
    \IfValueT{#5}{\label{#5}}% Possible \label
  \end{figure}
}

información relacionada