Me gustaría definir un comando que imprima la primera letra de su argumento como superíndice y las dos últimas letras como subscripción. Entonces si escribo:
\mynewcommand{abcde}
debería hacer lo mismo que
\textsuperscript{a}bc\textsubscript{de}
un comando así me ahorraría horas de tiempo, pero no sé cómo hacerlo
Editar: Lo siento, probablemente no lo tuve claro, la parte del medio puede serlo todo. Así que sólo la primera letra debe ser un superíndice y las dos últimas un subíndice.
Lo que necesito es:
\anothernewcommand{a some text that can contain \textit{other commands} cd}
que debería hacer lo mismo que
\textsuperscript{a} some text that can contain \textit{other commands} \textsubscript{cd}
Respuesta1
Creo que una sintaxis como \mynewcommand{a}{bc}{de}
sería más clara. De todos modos, puedo ofrecer dos implementaciones que se diferencian en el tratamiento de los espacios después del superíndice y antes del subíndice. Elige tu opción.
\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}
Más información. La función \tl_range:nnn
toma tres argumentos donde el primero es texto, el segundo y el tercero son números enteros que especifican el rango a extraer; so {1}{1}
extrae el primer elemento (también puede ser \tl_head:n
, pero usé la función más compleja para uniformidad), mientras que {-2}{-1}
especifica los dos últimos elementos (con índices negativos la extracción comienza desde el final); {2}{-3}
especifica el rango desde el segundo elemento hasta el tercero comenzando desde la derecha.
Sin embargo, para mantener espacios en los límites de las partes extraídas, primero debemos reemplazar los espacios con \c_space_tl
, que se expandirá a un espacio, pero las funciones de extracción no lo recortarán. La sintaxis de \tl_set:Nnn
es la misma, sólo que el primer argumento tiene que ser una variable tl.
Respuesta2
En aras de la complejidad, muestro cómo resolver este problema en el nivel primitivo de 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
Respuesta3
En aras de la variedad, aquí tienes una solución basada en LuaLaTeX. Configura una función de Lua que, a su vez, utiliza las funciones de cadena de Lua string.sub
y string.len
realiza su tarea. También configura una macro "contenedor" de LaTeX llamada \mynewcommand
, que expande su argumento una vez antes de pasarlo a la función Lua.
La solución en realidad emplea variantes de las funciones de cadena de Lua unicode.utf8.sub
y unicode.utf8.len
, para permitir que el argumento de \mynewcommand
sea cualquier cadena válida de caracteres codificados en utf8. (Por supuesto, paraimprimirlos caracteres de la cadena, se debe cargar una fuente adecuada). El argumento de \mynewcommand
puede contener primitivas y macros.
% !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}