Вложенные \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{

Вам необходимо изменить catcode пробела перед \newcommandи \capitalizeпотребуется изменить 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.upperи tex.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,s приложение делает слова заглавными. titlecapsПакет делает это с помощью \titlecapмакроса. Он обеспечивает большую гибкость в аргументе, включая изменение стиля и размера шрифта. Он также позволяет вам устанавливать слова исключения, которые не будут написаны с заглавной буквы (за исключением необязательного первого слова аргумента). Он может в значительной степени обойти начальные знаки препинания (например, скобки и квадратные скобки) при написании слов заглавными буквами и т. д. Он может делать заглавными диакритические знаки и т. д.

В MWE я показываю ваш пример «Hello World», а затем использую пример из документации пакета.

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

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

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