Es un poco artificial, pero asume lo siguiente como contexto:
Primero, quiero agregar etiquetas para tablas como tab:label
, para figuras como fig:label
, etc. En segundo lugar, quiero crear algunos comandos para incluir figuras, tablas, etc.
%{caption; label} %table def %table content
\NewDocumentCommand{\includetable}{> { \SplitArgument { 1 } { ; } } m m m}{
\begin{table}[htb!]
\captionlabel#1 % the Question is about this line; how do I pass the prefix?
\begin{tabular}{#2}
#3
\end{tabular}
\end{table}
}
Y tengo un comando que inserta un título y una etiqueta, que tiene el siguiente aspecto:
%caption %label prefix %label
\NewDocumentCommand{\captionlabel}{m O{} m}{
\caption{#1}\label{#2#3}
}
La pregunta es si de alguna manera es posible llamar \captionlabel
desde \includetable
y pasar el tab:
prefijo (algo así \captionlabel {#1.1}[tab:]{#1.2}
).
Sé que podría eliminar el argumento opcional \captionlabel
y llamar siempre \includetable
con tab:label
, o podría cambiar el orden de los argumentos de \captionlabel
a {O{} m m}
o {m m O{}}
, o insertar el comando. Básicamente es sólo un ejemplo.
Respuesta1
\NewDocumentCommand
es para definir comandos orientados al usuario, pero las cosas son más fáciles y limpias cuando expl3
define una API a nivel de código en la que se basan los comandos a nivel de usuario. Una de las razones es que es muy fácil generar variantes de funciones a nivel de código con \cs_generate_variant:Nn
, mientras que los comandos definidos con \NewDocumentCommand
no lo admiten. Aquí hay dos formas de resolver su problema que mantienen \captionlabel
intacta la interfaz de su comando.
Primera solución
Esta solución utiliza una función a nivel de código \fabian_insert_caption_and_label:nnn
donde los dos argumentos proporcionados vía \SplitArgument
ocupan posiciones adyacentes (es decir, las posiciones 2 y 3):
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new_protected:Npn \fabian_insert_caption_and_label:nnn #1#2#3
{ \caption {#2} \label {#1#3} }
\NewDocumentCommand { \includetable } { > { \SplitArgument { 1 } { ; } } m m m }
{
\begin{table}[htb!]
\centering
\fabian_insert_caption_and_label:nnn { tab: } #1
\begin{tabular}{#2}
#3
\end{tabular}
\end{table}
}
% Useless here, but maybe you want it for other code of yours.
\NewDocumentCommand { \captionlabel } { m O{} m }
{
\fabian_insert_caption_and_label:nnn {#2} {#1} {#3}
}
\ExplSyntaxOff
\begin{document}
\includetable{Caption text; the label}{ll}{This & is\\ a & table}
See table~\ref{tab:the label} on page~\pageref{tab:the label}.
\end{document}
Segunda solución
Esta solución utiliza una función a nivel de código \fabian_insert_caption_and_label:nnn
diferente a la de la primera solución: acepta los argumentos en el mismo orden que su archivo \captionlabel
. Para gestionar los dos “argumentos no adyacentes”, los pasamos manualmente después de dividir nosotros mismos el primer argumento de \includetable
. Tenga en cuenta que podría, y probablemente debería, crear una función a nivel de código correspondiente a this \includetable
. De esta manera, reutilizar el código es más fácil (no lo hice para no mezclar todos los problemas, pero estoy hablando de un simple contenedor: ver más abajo).
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\cs_new_protected:Npn \fabian_insert_caption_and_label:nnn #1#2#3
{ \caption {#1} \label {#2#3} }
\cs_generate_variant:Nn \fabian_insert_caption_and_label:nnn { xnx }
\seq_new:N \l_fabian_tmp_seq
\NewDocumentCommand { \includetable } { m m m }
{
\seq_set_split:Nnn \l_fabian_tmp_seq { ; } {#1}
\begin{table}[htb!]
\centering
\fabian_insert_caption_and_label:xnx
{ \seq_item:Nn \l_fabian_tmp_seq { 1 } }
{ tab: }
{ \seq_item:Nn \l_fabian_tmp_seq { 2 } }
\begin{tabular}{#2}
#3
\end{tabular}
\end{table}
}
\NewDocumentCommand { \captionlabel } { m O{} m }
{
\fabian_insert_caption_and_label:nnn {#1} {#2} {#3}
}
\ExplSyntaxOff
\begin{document}
\includetable{Caption text; the label}{ll}{This & is\\ a & table}
See table~\ref{tab:the label} on page~\pageref{tab:the label}.
\end{document}
Si la velocidad es una preocupación, usaría \seq_pop:NN
dos veces en lugar de \seq_item:Nn
dos veces, porque esta última itera sobre toda la secuencia cada vez, mientras que \seq_pop:NN
solo almacena y elimina el primer elemento, lo cual es muy rápido (el resultado es \q_no_value
cuando ya no hay ningún elemento para mostrar). ).
Envoltorio sugerido para\includetable
Cuando dije anteriormente que sugeriría que agregara una función simple a nivel de código correspondiente a su \includetable
, lo que quise decir fue simplemente algo como esto:
\cs_new_protected:Npn \fabian_include_table:nnn #1#2#3
{
\seq_set_split:Nnn \l_fabian_tmp_seq { ; } {#1}
\begin{table}[htb!]
\centering
\fabian_insert_caption_and_label:xnx
{ \seq_item:Nn \l_fabian_tmp_seq { 1 } }
{ tab: }
{ \seq_item:Nn \l_fabian_tmp_seq { 2 } }
\begin{tabular}{#2}
#3
\end{tabular}
\end{table}
}
\NewDocumentCommand { \includetable } { m m m }
{
\fabian_include_table:nnn {#1} {#2} {#3}
}
De esta manera, puedes definir muy fácilmente variantes de \fabian_include_table:nnn
uso \cs_generate_variant:Nn
. También puede cambiar su interfaz si es necesario (suponiendo que no sea parte de un paquete público) sin cambiar la interfaz del comando de nivel de usuario \includetable
.
Salida de ambos ejemplos.
Respuesta2
No puedo recomendar tal sintaxis. Sólo por interés académico, así es como puede hacerlo:
\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand{\includetable}{> { \SplitArgument { 2 } { ; } } m m m}{%
\begin{table}[htb!]
\centering
\addcaptionlabel#1
\begin{tabular}{#2}
#3
\end{tabular}
\end{table}
}
\NewDocumentCommand{\captionlabel}{m O{} m}{%
\caption{#1}\label{#2#3}
}
\NewDocumentCommand{\addcaptionlabel}{mmm}{%
\IfNoValueTF{#3}{%
\captionlabel{#1}{#2}%
}{%
\captionlabel{#1}[#2]{#3}%
}%
}
\begin{document}
\ref{DEF} and \ref{prefixDEF}
\includetable{ABC;DEF}{cc}{11 & 22 \\ 333 & 4}
\includetable{ABC;prefix;DEF}{cc}{11 & 22 \\ 333 & 4}
\end{document}