Ser ou não ser - Argumentos opcionais dentro da definição da macro

Ser ou não ser - Argumentos opcionais dentro da definição da macro

A parte da versão A no exemplo a seguir é definida header/footerpor macros do pacote fancyhdr.

Embora esta versão seja viável, é uma conquista estranha porque a mesma parte do código( \ifnum...) é escrita duas vezes.

Exceto o argumento opcional nº 2, todos os outros códigos são exatamente iguais. Para melhorar, tentei a versão B abaixo, mas não funciona de jeito nenhum.

Será horrível, especialmente quando a mesma parte do código for grande.

Então, para resumir, existe uma maneira melhor de lidar com argumentos opcionais (estar lá ou não) para evitar código duplicado pesado.

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

insira a descrição da imagem aqui

Responder1

Acredito que não seja útil tentar encaixar uma sintaxe estranha em um comando como esse.

É mais gratificante gastar um pouco mais de tempo desenvolvendo uma sintaxe mais amigável. Aqui eu proponho

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

Apenas um entre headou footdeve ser usado em uma chamada de \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}

insira a descrição da imagem aqui

Para responder sua pergunta: use uma macro para o código duplicado.

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

Vocêpodersubstitua a definição aqui por

\ExplSyntaxOn

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

\ExplSyntaxOff

mas não é algo que eu faria sozinho. O código \IfNoValueTF{#2}{}{[#2]}pode ser substituído de forma mais simples por \IfValueT{#2}{[#2]}.

Responder2

Para o exemplo específico, simplesmente usar \thispagestylena primeira página provavelmente seria melhor, mas para a questão geral, você pode evitar a pré-digitalização dos argumentos até depois dos testes para argumentos opcionais, e uma macro auxiliar pode evitar a duplicação de código:

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

informação relacionada