Macro expansível com loops e funções avançadas de string?

Macro expansível com loops e funções avançadas de string?

Este pode ser um problema muito básico (como uma continuação doessa questão), mas não consigo descobrir como fazer isso: preciso de uma macro \mycmd{sometext}que produza uma string dependendo do primeiro caractere de seu único argumento. Esta string resultante deve posteriormente ser capitalizada por \makefirstuc.

\documentclass{article}
\usepackage{xparse}  % used in example below
\usepackage{pgffor}  % used in example below
\usepackage{xstring} % used in example below

\newcommand{\mycmd}[1]{
  % here, I define a list of letters {a,e,i,o,u,A,E,I,O,U}
  % that will lead to the output "an " in case
  % the argument string starts by one of them.
  % Otherwise the output shall be "a ".
}

\begin{document}
  \mycmd{somestring}                % should print "a "
  \makefirstuc{\mycmd{sometext}}    % should print "A "
  \mycmd{otherstring}               % should print "an "
  \makefirstuc{\mycmd{otherstring}} % should print "An "
\end{document}

Tentei perceber isso usando o pfgforpacote para o loop e comparando o primeiro caractere com \IfBeginWitho do xstringpacote. No entanto, isso aparentemente leva a comandos não expansíveis, de modo que \makefirstucfalha.Como conseguir um comando expansível com esta funcionalidade?

Até agora, criei o seguinte comando não expansível:

\NewDocumentCommand{\mycmd}{m}{%
  \def\undefart{a\space}%
  \foreach \c in {a,e,i,o,u,A,E,I,O,U}{%
    \IfBeginWith{#1}{\c}{\global\def\undefart{an\space}}{}%
  }%
  \undefart%
}

Responder1

Isso é bastante fácil de usar expl3(existem várias abordagens possíveis):

\documentclass{article}
\usepackage{expl3,xparse}
\ExplSyntaxOn
\DeclareExpandableDocumentCommand \mycmd { m }
  {
    \__mycmd_loop:nN {#1} aeiouAEIOU \q_recursion_tail \q_recursion_stop
  }
\cs_new:Npn \__mycmd_loop:nN #1#2
  {
    \quark_if_recursion_tail_stop_do:nn {#2} { a }
    \tl_if_head_eq_charcode:nNT {#1} #2
      {
        \use_i_delimit_by_q_recursion_stop:nw { an }
      }
    \__mycmd_loop:nN {#1}
  }
\ExplSyntaxOff
\usepackage{mfirstuc}
\begin{document}

  \mycmd{somestring}                % should print "a "
  \emakefirstuc{\mycmd{sometext}}    % should print "A "
  \mycmd{otherstring}               % should print "an "
  \emakefirstuc{\mycmd{otherstring}} % should print "An "
\end{document}

Como \makefirstucnão há expansão, tive que usar a eversão. Eu provavelmente preferiria contornar isso usando o expansível (e compatível com Unicode) \text_titlecase:n:

\documentclass{article}
\usepackage{expl3,xparse}
\ExplSyntaxOn
\DeclareExpandableDocumentCommand \mycmd { m }
  {
    \__mycmd_loop:nN {#1} aeiouAEIOU \q_recursion_tail \q_recursion_stop
  }
\cs_new:Npn \__mycmd_loop:nN #1#2
  {
    \quark_if_recursion_tail_stop_do:nn {#2} { a }
    \tl_if_head_eq_charcode:nNT {#1} #2
      {
        \use_i_delimit_by_q_recursion_stop:nw { an }
      }
    \__mycmd_loop:nN {#1}
  }
\cs_new_eq:NN \Mymakefirstuc \text_uppercase:n
\ExplSyntaxOff
\begin{document}

  \mycmd{somestring}                % should print "a "
  \Mymakefirstuc{\mycmd{sometext}}    % should print "A "
  \mycmd{otherstring}               % should print "an "
  \Mymakefirstuc{\mycmd{otherstring}} % should print "An "
\end{document}

Dependendo do número de casos, pode ser desejável colocar todas as entradas em minúsculas primeiro

\DeclareExpandableDocumentCommand \mycmd { m }
  {
    \exp_args:Nf \__mycmd:n { \text_lowercase:n {#1} }
  }
\cs_new:Npn \__mycmd:n #1
  {
    \__mycmd_loop:nN {#1} aeiou \q_recursion_tail \q_recursion_stop
  }

Responder2

Aqui está uma abordagem comexpl3

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

\ExplSyntaxOn
\DeclareExpandableDocumentCommand{\indef}{m}
 {
  \str_case_x:nnF { \tl_head:f { \tl_lower_case:n { #1 } } }
   {
    {a}{an}
    {e}{an}
    {i}{an}
    {o}{an}
    {u}{an}
  }
  {a}~#1
 }
\ExplSyntaxOff

\begin{document}

\indef{abc} --- \indef{cde} --- \indef{ABC} --- \indef{CDE}

\emakefirstuc{\indef{abc}} --- \emakefirstuc{\indef{cde}} ---
\emakefirstuc{\indef{ABC}} --- \emakefirstuc{\indef{CDE}}

\end{document}

insira a descrição da imagem aqui

Responder3

Aqui está uma solução baseada em LuaLaTeX. Ele define duas macros "wrapper" totalmente expansíveis denominadas \mycmdand \mkfirstuc, que passam seus argumentos para funções Lua denominadas mycmdand mkfirstuc. As funções Lua realizam o trabalho real de prefixar "an" ou "a" em uma string e colocar o primeiro caractere em maiúscula na string, respectivamente.

insira a descrição da imagem aqui

% !TEX TS-program = lualatex
\documentclass{article}

%% Lua-side code
\usepackage{luacode}
\begin{luacode}
function mycmd ( s )
  if string.match ( string.sub(s,1,1) , "[aeiouAEIOU]" ) then
    return tex.sprint ("an " .. s)
  else
    return tex.sprint ("a " .. s)
  end
end
function mkfirstuc ( s )
  return tex.sprint ( string.upper(string.sub(s,1,1)) .. string.sub(s,2) )
end
\end{luacode}

%% TeX-side code
\newcommand\mycmd[1]{\directlua{mycmd(\luastring{#1})}}
\newcommand\mkfirstuc[1]{\directlua{mkfirstuc(\luastring{#1})}}

\begin{document}
\mycmd{abc}, \mycmd{def}, \mycmd{ABC}, \mycmd{DEF}.

\mkfirstuc{\mycmd{abc}}, \mkfirstuc{\mycmd{def}}, 
\mkfirstuc{\mycmd{ABC}}, \mkfirstuc{\mycmd{DEF}}. 
\end{document}

Responder4

É isso que significa? Não é o uso mais eficaz de expl3, eu sei ;-)

\documentclass{article}
\usepackage{xparse}  % used in example below
\usepackage{pgffor}  % used in example below
\usepackage{xstring} % used in example below

\ExplSyntaxOn

\clist_set:Nn \l_tinytot_lowercaseletters_clist {a,e,i,o,u}
\clist_set:Nn \l_tinytot_letters_clist {a,e,i,o,u,A,E,I,O,U}

\NewDocumentCommand{\makefirstuc}{m}{%
  \tl_to_uppercase:n {#1}
}

\NewDocumentCommand{\checkstart}{m}{%
  \tl_set:Nx \l_tmpa_tl {#1}
  \tl_trim_spaces:N \l_tmpa_tl
  \tl_set:Nx \l_tmpb_tl { \tl_item:Nn \l_tmpa_tl {1}}
  \clist_if_in:NVTF \l_tinytot_letters_clist {\l_tmpb_tl }{%
    \clist_if_in:NVTF \l_tinytot_lowercaseletters_clist {\l_tmpb_tl}
    {
      an
    }{
      An
    }
  }{
    \clist_if_in:NVTF \l_tinytot_lowercaseletters_clist {\l_tmpb_tl}
    {
      a
    }{
      A
    }
  }
}  
\ExplSyntaxOff

\newcommand{\mycmd}[1]{%
  \checkstart{#1}
  % here, I define a list of letters {a,e,i,o,u,A,E,I,O,U}
  % that will lead to the output "an " in case
  % the argument string starts by one of them.
  % Otherwise the output shall be "a ".
}

\begin{document}
\mycmd{somestring}                % should print "a "
\makefirstuc{\mycmd{sometext}}    % should print "A "
\mycmd{otherstring}               % should print "an "
\makefirstuc{\mycmd{otherstring}} % should print "An "
\end{document}

informação relacionada