Это немного искусственно, но предположим следующее в качестве контекста:
Во-первых, я хочу добавить метки для таблиц как tab:label
, для рисунков как fig:label
и т. д. Во-вторых, я хочу создать несколько команд для включения рисунков, таблиц и т. д.
%{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}
}
У меня есть команда, которая вставляет заголовок и метку, которая выглядит следующим образом:
%caption %label prefix %label
\NewDocumentCommand{\captionlabel}{m O{} m}{
\caption{#1}\label{#2#3}
}
Вопрос в том, можно ли как-то позвонить \captionlabel
и \includetable
передать tab:
префикс (что-то вроде \captionlabel {#1.1}[tab:]{#1.2}
).
Я знаю, что я мог бы удалить необязательный аргумент в \captionlabel
и всегда вызывать \includetable
с tab:label
, или я мог бы поменять порядок аргументов на \captionlabel
или {O{} m m}
, {m m O{}}
или встроить команду. Это по сути просто пример.
решение1
\NewDocumentCommand
предназначен для определения команд, обращенных к пользователю, но все становится проще и чище, expl3
когда вы определяете API уровня кода, на котором основаны команды уровня пользователя. Одна из причин заключается в том, что очень легко генерировать варианты функций уровня кода с помощью \cs_generate_variant:Nn
, тогда как команды, определенные с помощью , \NewDocumentCommand
не поддерживают этого. Вот два способа решения вашей проблемы, которые сохраняют интерфейс вашей \captionlabel
команды нетронутым.
Первое решение
В этом решении используется функция уровня кода \fabian_insert_caption_and_label:nnn
, в которой два аргумента, предоставленные через, \SplitArgument
занимают соседние позиции (а именно, позиции 2 и 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}
Второе решение
Это решение использует функцию уровня кода, \fabian_insert_caption_and_label:nnn
отличную от той, что была в первом решении: она принимает аргументы в том же порядке, что и ваш \captionlabel
. Чтобы управлять двумя «несмежными аргументами», мы передаем их вручную после разделения первого аргумента \includetable
. Обратите внимание, что вы могли бы и, вероятно, должны создать функцию уровня кода, соответствующую этому \includetable
. Таким образом, повторное использование кода становится проще (я не делал этого, чтобы не смешивать все проблемы, но я говорю всего лишь о простой обертке: см. ниже).
\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}
Если скорость важна, я бы использовал \seq_pop:NN
twice вместо \seq_item:Nn
twice, потому что последний каждый раз повторяет всю последовательность, тогда как \seq_pop:NN
просто сохраняет и удаляет первый элемент, что очень быстро (результатом является \q_no_value
то, что больше нет элементов для извлечения).
Предлагаемая обертка для\includetable
Когда я сказал выше, что предлагаю вам добавить простую функцию на уровне кода, соответствующую вашему \includetable
, я имел в виду что-то вроде этого:
\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}
}
Таким образом, вы можете очень легко определить варианты \fabian_include_table:nnn
использования \cs_generate_variant:Nn
. Вы также можете изменить его интерфейс, если это необходимо (предполагая, что он не является частью публичного пакета), не меняя интерфейс команды уровня пользователя \includetable
.
Вывод обоих примеров
решение2
Я не могу рекомендовать такой синтаксис. Просто ради академического интереса, вот как можно сделать:
\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}