Быть или не быть — Необязательные аргументы внутри определения макроса

Быть или не быть — Необязательные аргументы внутри определения макроса

Часть версии A в следующем примере определяется header/footerмакросами из пакета fancyhdr.

Хотя эта версия работоспособна, это неудобное достижение, поскольку одна и та же часть кода ( \ifnum...) написана дважды.

За исключением необязательного аргумента #2, весь остальной код абсолютно такой же. Чтобы улучшить его, я попробовал версию B ниже, но она вообще не работает.

Это будет ужасно, особенно если та же часть кода большая.

Итак, подведем итог: существует ли лучший способ обработки необязательных аргументов (присутствовать или нет), чтобы избежать большого количества дублирующего кода?

\documentclass{article}
\usepackage{geometry,fancyhdr,xparse}
\geometry{showframe}

\begin{document}
first page\par\vspace{80em} second page
\pagestyle{fancy}
\fancyhf{}

%Version A: workable %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\NewDocumentCommand{\firstpage}{momm}{%
  \IfNoValueTF{#2}
  {#1{%
      \ifnum\value{page}=1
      #3% content of header/footer only at the first page
      \else
      #4% content of header/footer at the rest pages
      \fi
    }
  }
  {#1[#2]% the only diferrence between the two branches of \IfNoValueTF is, whether introducing the optional argument #2
    {%
      \ifnum\value{page}=1
      #3% content of header/footer only at the first page
      \else
      #4% content of header/footer at the rest pages
      \fi
    }
  }
}%\firstpage

% Test
\firstpage{\fancyhead}[c]{only at the first page}{at rest pages}
\firstpage{\rhead}{r first}{r rest}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Version B: not workable %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\NewDocumentCommand{\firstpageA}{momm}{%
   #1 \IfNoValueTF{#2}{}{[#2]}
      \ifnum\value{page}=1
      #3% content of header/footer only at the first page
      \else
      #4% content of header/footer at the rest pages
      \fi
}%\firstpageA

% Test
\firstpageA{\fancyhead}[c]{only at the first page}{at rest pages}
\firstpageA{\rhead}{r first}{r rest}  

\end{document}

введите описание изображения здесь

решение1

Я считаю, что бесполезно пытаться втиснуть неудобный синтаксис в такую ​​команду.

Гораздо полезнее потратить немного больше времени на разработку более удобного синтаксиса. Здесь я предлагаю

\sethf{
  head = <contents of all headers, except possibly the first>,
  foot = <contents of all footers, except possibly the first>,
  pos = <l|c|r>,
  first = <possible exception for first header/footer>,
}

При вызове следует использовать только один из headили .foot\sethf

\documentclass{article}
\usepackage{fancyhdr,xparse}

\usepackage[paper=a6paper]{geometry} % smaller pictures
\usepackage{lipsum} % mock text

\pagestyle{fancy}
\fancyhf{}

\ExplSyntaxOn
\NewDocumentCommand{\sethf}{m}
 {
  \tl_clear:N \l__lyl_hf_pos_tl
  \tl_clear:N \l__lyl_hf_first_tl
  \tl_clear:N \l__lyl_hf_headfoot_tl
  \keys_set:nn { lyl/hf } { #1 }
  \lyl_hf_set:
 }

\keys_define:nn { lyl/hf }
 {
  head  .code:n   = \__lyl_hf_aux:Nn \fancyhead { #1 },
  foot  .code:n   = \__lyl_hf_aux:Nn \fancyfoot { #1 },
  pos   .tl_set:N = \l__lyl_hf_pos_tl,
  first .tl_set:N = \l__lyl_hf_first_tl,
 }

\tl_new:N \l__lyl_hf_headfoot_tl

\cs_new_protected:Nn \__lyl_hf_aux:Nn
 {
  \cs_set_eq:NN \__lyl_hf_temp:w #1
  \tl_set:Nn \l__lyl_hf_headfoot_tl { #2 }
 }

\cs_new_protected:Nn \lyl_hf_set:
 {
  \tl_if_empty:NT \l__lyl_hf_first_tl
   {
    \tl_set_eq:NN \l__lyl_hf_first_tl \l__lyl_hf_headfoot_tl
   }
  \__lyl_hf_set:NVVV 
   \__lyl_hf_temp:w        % \fancyhead or \fancyfoot
   \l__lyl_hf_pos_tl       % position
   \l__lyl_hf_first_tl     % first page
   \l__lyl_hf_headfoot_tl  % other pages
 }
\cs_new_protected:Nn \__lyl_hf_set:Nnnn
 {
  #1 [ #2 ] { \int_compare:nTF { \value{page} = 1 } { #3 } { #4 } }
 }
\cs_generate_variant:Nn \__lyl_hf_set:Nnnn { NVVV }
\ExplSyntaxOff

\sethf{
  head = other pages,
  first = first page,
  pos = c,
}

\sethf{pos = r, foot=\thepage}

\begin{document}

\lipsum[1-2]

\end{document}

введите описание изображения здесь

Чтобы ответить на ваш вопрос: используйте макрос для дублирующего кода.

\documentclass{article}
\usepackage{fancyhdr,xparse}

\usepackage[paper=a6paper]{geometry} % smaller pictures
\usepackage{lipsum} % mock text

\pagestyle{fancy}
\fancyhf{}

\NewDocumentCommand{\firstpage}{momm}{%
  \IfNoValueTF{#2}
   {\setfirstpage{#1}{#3}{#4}}
   {\setfirstpage{#1[#2]}{#3}{#4}}%
}
\NewDocumentCommand{\setfirstpage}{mmm}
 {
  #1{\ifnum\value{page}=1 #2\else #3\fi}
 }

\firstpage{\fancyhead}[c]{first page}{other pages}
% can also be \firstpage{\chead}{first page}{other pages}

\begin{document}

\lipsum[1-2]

\end{document}

Тымощьзамените определение здесь на

\ExplSyntaxOn

\NewDocumentCommand{\firstpage}{momm}
 {
  \exp_last_unbraced:Nf #1 \IfNoValueTF{#2}{}{[#2]}
   {
    \int_compare:nTF { \value{page}=1 } { #3 } { #4 } 
   }
 }

\ExplSyntaxOff

но это не то, что я бы сделал сам. Код \IfNoValueTF{#2}{}{[#2]}можно проще заменить на \IfValueT{#2}{[#2]}.

решение2

Для конкретного примера, \thispagestyleвероятно, было бы лучше просто использовать на первой странице, но для общего вопроса вы можете избежать предварительного сканирования аргументов до тех пор, пока не будут проверены необязательные аргументы, а вспомогательный макрос поможет избежать дублирования кода:

\documentclass{article}
\usepackage{geometry,fancyhdr,xparse}
\geometry{showframe}

\begin{document}
first page\par\vspace{80em} second page
\pagestyle{fancy}
\fancyhf{}


\def\fpageTF#1#2#3{#1{\ifnum\value{page}=1 #2\else #3\fi}}



\NewDocumentCommand{\firstpage}{mo}{%
  \IfNoValueTF{#2}{\fpageTF{#1}}{\fpageTF{#1[#2]}}}


% Test
\firstpage{\fancyhead}[c]{only at the first page}{at rest pages}
\firstpage{\rhead}{r first}{r rest}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\end{document}

Связанный контент