Автоматизированные верхние и нижние индексы

Автоматизированные верхние и нижние индексы

Я хотел бы определить команду, которая печатает первую букву своего аргумента как верхний индекс, а последние две буквы как подстрочный. Так что если я введу:

\mynewcommand{abcde}

он должен делать то же самое, что и

\textsuperscript{a}bc\textsubscript{de}

такая команда сэкономила бы мне кучу времени, но я не знаю как это сделать

Редактировать: Извините, я, вероятно, не ясно выразился, часть в середине может быть всем. Так что только первая буква должна быть надстрочным индексом, а последние две — подстрочным индексом.

Мне нужно следующее:

\anothernewcommand{a some text that can contain \textit{other commands} cd}

который должен делать то же самое, что и

\textsuperscript{a} some text that can contain \textit{other commands} \textsubscript{cd}

решение1

Я считаю, что такой синтаксис \mynewcommand{a}{bc}{de}был бы понятнее. В любом случае, я могу предложить две реализации, которые отличаются обработкой пробелов после верхнего индекса и перед нижним индексом. Выбирайте.

\documentclass{article}

%\usepackage{xparse} % not needed for LaTeX 2020-10-01

\ExplSyntaxOn
\NewDocumentCommand{\mynewcommandA}{m}
 {
  \textsuperscript{\tl_range:nnn { #1 } { 1 } { 1 } }
  \tl_range:nnn { #1 } { 2 } { -3 }
  \textsubscript{\tl_range:nnn { #1 } { -2 } { -1 } }
 }

\NewDocumentCommand{\mynewcommandB}{m}
 {
  \tl_set:Nn \l_tmpa_tl { #1 }
  \tl_replace_all:Nnn \l_tmpa_tl { ~ } { \c_space_tl }
  \textsuperscript{\tl_range:Nnn \l_tmpa_tl { 1 } { 1 } }
  \tl_range:Nnn \l_tmpa_tl { 2 } { -3 }
  \textsubscript{\tl_range:Nnn \l_tmpa_tl { -2 } { -1 } }
}
\ExplSyntaxOff

\begin{document}

\textbf{Leading and trailing spaces are not kept}

\mynewcommandA{abcde}

\mynewcommandA{a some text that can contain \textit{other commands} cd}

\bigskip

\textbf{Leading and trailing spaces are kept}

\mynewcommandB{abcde}

\mynewcommandB{a some text that can contain \textit{other commands} cd}

\end{document}

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

Еще немного информации. Функция \tl_range:nnnпринимает три аргумента, где первый — это некоторый текст, второй и третий — целые числа, которые определяют диапазон для извлечения; поэтому {1}{1}извлекает первый элемент (это может быть и \tl_head:n, но я использовал более сложную функцию для единообразия), тогда как {-2}{-1}определяет последние два элемента (при отрицательных индексах извлечение начинается с конца); {2}{-3}определяет диапазон от второго элемента до третьего, начиная справа.

Однако, чтобы сохранить пробелы на границах извлеченных частей, нам нужно сначала заменить пробелы на \c_space_tl, что расширится до пробела, но не будет обрезано функциями извлечения. Синтаксис \tl_set:Nnnтот же, только первый аргумент должен быть переменной tl.

решение2

Для простоты я покажу, как решить эту задачу на примитивном уровне TeX:

\newcount\bufflen

\def\splitbuff #1#2{% #1: number of tokens from end, #2 data
                    % result: \buff, \restbuff
    \edef\buff{\detokenize{#2} }%
    \edef\buff{\expandafter}\expandafter\protectspaces \buff \\
    \bufflen=0 \expandafter\setbufflen\buff\end
    \advance\bufflen by-#1\relax
    \ifnum\bufflen<0 \errmessage{#1>buffer length}\fi
    \ifnum\bufflen>0 \edef\buff{\expandafter}\expandafter\splitbuffA \buff\end
    \else \let\restbuff=\buff \def\buff{}\fi
    \edef\tmp{\gdef\noexpand\buff{\buff}\gdef\noexpand\restbuff{\restbuff}}%
    {\endlinechar=-1 \scantokens\expandafter{\tmp}}%
}

\def\protectspaces #1 #2 {\addto\buff{#1}%
    \ifx\\#2\else \addto\buff{{ }}\afterfi \protectspaces #2 \fi}  
\def\afterfi #1\fi{\fi#1}
\long\def\addto#1#2{\expandafter\def\expandafter#1\expandafter{#1#2}}

\def\setbufflen #1{%
    \ifx\end#1\else \advance\bufflen by1 \expandafter\setbufflen\fi}

\def\splitbuffA #1{\addto\buff{#1}\advance\bufflen by-1
    \ifnum\bufflen>0 \expandafter\splitbuffA
    \else \expandafter\splitbuffB \fi
}
\def\splitbuffB #1\end{\def\restbuff{#1}}

% --------------- \mynewcommand implementation:

\def\textup#1{$^{\rm #1}$}  \def\textdown#1{$_{\rm #1}$}
\def\mynewcommand#1{\mynewcommandA#1\end}
\def\mynewcommandA#1#2\end{%
   \textup{#1}\splitbuff 2{#2}\buff \textdown{\restbuff}}

% --------------- test:

\mynewcommand{abcde}

\mynewcommand{a some text that can contain {\it other commands} cd}

\bye

решение3

Ради разнообразия, вот решение на основе LuaLaTeX. Оно устанавливает функцию Lua, которая, в свою очередь, использует строковые функции Lua string.subи string.lenдля выполнения своей задачи. Оно также устанавливает макрос-"обертку" LaTeX, называемый \mynewcommand, который один раз расширяет свой аргумент перед передачей его в функцию Lua.

Решение фактически использует варианты строковых функций Lua unicode.utf8.subи unicode.utf8.len, чтобы аргумент мог \mynewcommandбыть любой допустимой строкой символов в кодировке utf8. (Конечно, для того, чтобыРаспечатать\mynewcommand( ( ...

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

% !TEX TS-program = lualatex
\documentclass{article}
\usepackage{luacode} % for "\luaexec" and "\luastringO" macros
\luaexec{
% Define a Lua function called "mycommand"
function mycommand ( s )
   local s1,s2,s3
   s1  = unicode.utf8.sub ( s, 1, 1 )
   s2  = unicode.utf8.sub ( s, 2, unicode.utf8.len(s)-2 )
   s3  = unicode.utf8.sub ( s, -2 )
   return ( "\\textsuperscript{" ..s1.. "}" ..s2.. "\\textsubscript{" ..s3.. "}" )
end
}
% Create a wrapper macro for the Lua function
\newcommand\mynewcommand[1]{\directlua{tex.sprint(mycommand(\luastringO{#1}))}}
    
\begin{document}
abcde $\to$ \mynewcommand{abcde}

öçäßüéà $\to$ \mynewcommand{öçäßüéà}

\mynewcommand{a some text that can contain \textit{\textbf{other commands}} cd}
\end{document}

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