具有循环和高级字符串函数的可扩展宏?

具有循环和高级字符串函数的可扩展宏?

这可能是一个非常基本的问题(作为这个问题) 但我不知道该怎么做:我需要一个宏\mycmd{sometext},根据其一个参数的第一个字符生成一个字符串。这个结果字符串随后应大写\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}

我尝试使用pfgfor循环包来实现这一点,并将第一个字符与包\IfBeginWith中的第一个字符进行比较xstring。然而,这显然会导致命令无法扩展,因此\makefirstuc失败了。如何利用此功能实现可扩展的命令?

到目前为止,我创建了以下不可扩展的命令:

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

答案1

这很容易使用expl3(有几种可能的方法):

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

由于\makefirstuc没有扩展,我不得不使用版本e。我可能更喜欢使用可扩展的(和 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}

根据情况的数量,可能需要先将所有输入都小写

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

答案2

以下是一种方法expl3

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

在此处输入图片描述

答案3

这是一个基于 LuaLaTeX 的解决方案。它定义了两个完全可扩展的“包装器”宏,名为\mycmd\mkfirstuc,它们将其参数传递给名为mycmd和的 Lua 函数mkfirstuc。Lu​​a 函数分别执行在字符串前添加“an”或“a”以及将字符串中的第一个字符大写的实际工作。

在此处输入图片描述

% !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}

答案4

expl3这就是 的意思吗?我知道这不是 的最有效用法;-)

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

相关内容