嵌套 \def 導致 \inaccessible 錯誤

嵌套 \def 導致 \inaccessible 錯誤

我嘗試了一種 - 至少在我看來 - 非常簡單的方法來大寫單字:

\documentclass{article}

\begin{document}
\begingroup%
\obeyspaces%\catcode`\ \active
\def {\space\MakeUppercase}%
Hello world
\endgroup
\end{document}

不用說,我也嘗試將其包裝成一個宏,例如

\documentclass{article}

\newcommand{\capitalize}[1]{\begingroup\obeyspaces\def {\space\MakeUppercase}#1\endgroup}

\begin{document}
\capitalize{Hello world}
\end{document}

這將導致 TeX 抱怨內部語法\def並使其\inaccessible.

究竟出了什麼問題,有沒有辦法解決這個問題?

答案1

這是\verb在爭論問題中通常不起作用的情況。

\obeyspaces改變空格的catcode,這意味著空格特點在一個文件轉換為活動令牌。目錄程式碼更改有對已建立的令牌的影響。在您的情況下, 的整個參數已被標記化,因此在後面\newcommand根本沒有空格標記。\def\def{

您需要在 之前更改 space 的 catcode,\newcommand並且\capitalize需要在取得其參數之前更改 space 的 catcode。 (出於這個原因和其他原因,我不會為此使用catcode更改,而是簡單地使用分隔參數來查找正常空格)

\documentclass{article}

\newcommand{\capitalize}[1]{\xcapitalize#1 \relax}
\def\xcapitalize#1 #2{%
#1%
\ifx\relax#2%
\else
\space\MakeUppercase{#2}%
\expandafter\xcapitalize
\fi}

\begin{document}
\capitalize{Hello world and this}
\end{document}

註解中要求的版本也將第一個字母大寫,並允許參數是巨集:

\documentclass{article}

\newcommand{\capitalize}[1]{\ignorespaces
\expandafter\expandafter\expandafter
\xcapitalize\expandafter\space #1 \relax}
\def\xcapitalize#1 #2{%
#1%
\ifx\relax#2%
\else
\space\MakeUppercase{#2}%
\expandafter\xcapitalize
\fi}

\begin{document}
\capitalize{hello world and this}

\newcommand\zzz{hello world and this}
\capitalize{\zzz}
\end{document}

答案2

該解決方案需要最近更新的 TeX 發行版。與 的方法相比,它的優點是\obeyspaces不會更改類別代碼,因此巨集也可以進入其他命令的參數。

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\capitalize}{m}
 {
  \ruben_capitalize:n { #1 }
 }

\seq_new:N \l__ruben_capitalize_words_seq
\seq_new:N \l__ruben_capitalize_out_seq

\cs_new_protected:Npn \ruben_capitalize:n #1
 {
  \seq_set_split:Nnn \l__ruben_capitalize_words_seq { ~ } { #1 }
  \seq_set_map:NNn \l__ruben_capitalize_out_seq \l__ruben_capitalize_words_seq
   {
    \tl_mixed_case:n { ##1 }
   }
  \seq_use:Nn \l__ruben_capitalize_out_seq { ~ }
 }
\ExplSyntaxOff

\begin{document}
\capitalize{Hello world}
\end{document}

如果您還希望能夠將作為巨集傳遞的字串大寫,只需將 的定義變更\capitalize

\NewDocumentCommand{\capitalize}{m}
 {
  \ruben_capitalize:o { #1 }
 }

並添加

\cs_generate_variant:Nn \ruben_capitalize:n { o }

在 的定義之後\ruben_capitalize:n(即在 之前\ExplSyntaxOff)。

完整範例:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\NewDocumentCommand{\capitalize}{m}
 {
  \ruben_capitalize:o { #1 }
 }

\seq_new:N \l__ruben_capitalize_words_seq
\seq_new:N \l__ruben_capitalize_out_seq

\cs_new_protected:Npn \ruben_capitalize:n #1
 {
  \seq_set_split:Nnn \l__ruben_capitalize_words_seq { ~ } { #1 }
  \seq_set_map:NNn \l__ruben_capitalize_out_seq \l__ruben_capitalize_words_seq
   {
    \tl_mixed_case:n { ##1 }
   }
  \seq_use:Nn \l__ruben_capitalize_out_seq { ~ }
 }
\cs_generate_variant:Nn \ruben_capitalize:n { o }
\ExplSyntaxOff

\newcommand{\myhello}{hello world}

\begin{document}
\capitalize{Hello world}

\capitalize{hello world}

\capitalize{\myhello}

\capitalize\myhello
\end{document}

在此輸入影像描述


經典方法\obeylines要求您發出它吸收論點:

% First setup obeyspace and give a meaning to active space
\newcommand{\capitalize}{\begingroup\obeyspaces\setupcapspace\docapitalize}
% Just absorb the argument and end the group
\newcommand{\docapitalize}[1]{#1\endgroup}
% Define (locally) the behavior of active space
\begingroup\lccode`~=`\ % <--- don't forget this one
  \lowercase{\endgroup\newcommand\setupcapspace{\def~{\space\MakeUppercase}}}

最後兩行也可以

{\obeyspaces\gdef\setupcapspace{\def {\space\MakeUppercase}}}

但此\lowercase方法避免了\obeyspaces雜散空間可能出現的問題。

然而,定界參數方法肯定更好,因為它允許\capitalize出現在其他命令的參數中。

答案3

基於 LuaLaTeX 的解。我們定義了一個名為的新宏\capitalize,它使用 Lua 函數string.uppertex.sprint。的參數\capitalize可以是硬編碼字串,也可以是可能產生字串的巨集。

在此輸入影像描述

% !TEX TS-program = lualatex
\documentclass{article}
{\catcode\%=12 
 \gdef\capitalize#1{
   \directlua{ str="#1"; 
               tex.sprint ( string.gsub(" "..str, "%W%l",
                string.upper):sub(2)) } }
} 
\begin{document}

\capitalize{Once upon a time there was a princess
  who lived in a great palace that was close to the 
  edge of a dark and mysterious forest.}
\end{document}

答案4

雖然您的問題是關於嵌套的\def,但應用程式正在將單字大寫。該titlecaps套件透過\titlecap巨集來完成此操作。它允許論證具有廣泛的靈活性,包括字體樣式和大小的變更。它還允許您將排除詞設為不大寫(除了可選地作為參數的第一個字)。它可以在很大程度上克服大寫單字等時的前導標點符號(如括號和方括號)。

在 MWE 中,我展示了「hello world」範例,然後使用套件文件中的 over-the-top 範例。

\documentclass{article}
\usepackage{titlecaps}
\def\bs{$\backslash$}

\begin{document}
\titlecap{hello world}

\Addlcwords{for a is but and with of in as the etc on to if}
\titlecap{% 
to know that none of the words typed in this paragraph were initially
upper cased might be of interest to you.  it is done to demonstrate the
behavioral features of this package.  first, you should know the words
that i have pre-designated as lower case.  they are:  ``for a is but and
with of in as the etc on to if.''  you can define your own list.  note
that punctuation, like the period following the word ``if'' did not mess
up the search for lower case (nor did the quotation marks just now).
punctuation which is screened out of the lower-cased word search pattern
include . , : ; ( ) [ ] ? ! ` ' however, I cannot screen text braces;
\{for example in\} is titled, versus (for example in), since the braces
are not screened out in the search for pre-designated lower-case words
like for and in.  However, \texttt{\bs textnc} provides a workaround:
\{\textnc{for example in}\}.  titlecap will consider capitalizing
following a (, [, \{, or - symbol, such as (abc-def).  you can use your
text\textit{\relax xx} commands, like i just did here with the prior xx,
but if you want the argument of that command to not be titled, you
either need, in this example, to add \textit{xx} to the lowercase word
list, which you can see i did not.  instead, i put ``\bs relax~xx'' as
the argument, so that, in essence, the \bs relax was capitalized, not
the x.  Or you could use \texttt{\bs textnc} .  here i demonstrate that
text boldface, \textbf{as in the \bs textbf command}, also works fine,
as do \texttt{texttt}, \textsl{textsl}, \textsc{textsc},
\textsf{textsf}, \textit{etc}.  titlecap will work on diacritical marks,
such as \"apfel, \c cacao \textit{etc.}, \scriptsize fontsize \LARGE
changing commands\normalsize\unskip, as well as national symbols such as
\o laf, \ae gis, and \oe dipus.  unfortunately, i could not get it to
work on the \aa~nor the \l~symbols. the method will work with some
things in math mode, capitalizing symbols if there is a leading space,
$x^2$ can become $ x^2$, and it can process but it will not capitalize
the greek symbols, such as $\alpha$, and will choke on most macros, if
they are not direct character expansions.  Additionally,
\textsf{titlecaps} also works with font changing declarations, for
example, \bs itshape\bs sffamily. \itshape\sffamily you can see that it
works fine.  likewise, any subsequent \bs textxx command will, upon
completion, return the font to its prior state, such as this
\textbf{textbf of some text}.  you can see that i have returned to the
prior font, which was italic sans-serif. now I will return to upright
roman\upshape\rmfamily.  a condition that will not behave well is inner
braces, such as \ttfamily \bs titlecap\{blah \{inner brace material\}
blah-blah\}. \rmfamily see the section on quirks and limitations for a
workaround involving \texttt{\bs textnc}.  titlecap will always
capitalize the first word of the argument (\textbf{even if it is on the
lower-case word list}), unless \texttt{\bs titlecap} is invoked with an
optional argument that is anything other than a capital p.  in that case,
the first word will be titled \textit{unless} it is on the lowercase
word list.  for example, i will do a \bs titlecap[\relax s]\{\relax
a~big~man\} and get ``\titlecap[s]\textnc{a big man}'' with the ``a''
not titled.  i hope this package is useful to you, but as far as using
\textsf{titlecaps} on such large paragraphs\ldots \textbf{do not try
this at home!}}
\end{document}

在此輸入影像描述

相關內容