Automatisierte Tief- und Hochstellungen

Automatisierte Tief- und Hochstellungen

Ich möchte einen Befehl definieren, der den ersten Buchstaben seines Arguments hochgestellt und die letzten beiden Buchstaben tiefgestellt ausgibt. Wenn ich also Folgendes eingebe:

\mynewcommand{abcde}

es sollte das gleiche tun wie

\textsuperscript{a}bc\textsubscript{de}

ein solcher Befehl würde mir Stunden an Zeit sparen, aber ich weiß nicht, wie ich das machen soll

Edit: Entschuldigung, ich habe mich wohl nicht klar ausgedrückt, der Teil in der Mitte kann alles sein. Also sollte nur der erste Buchstabe hochgestellt sein und die letzten beiden tiefgestellt.

Was ich brauche ist:

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

die dann das gleiche tun sollten wie

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

Antwort1

Ich glaube, dass eine Syntax wie \mynewcommand{a}{bc}{de}klarer wäre. Jedenfalls kann ich zwei Implementierungen anbieten, die sich in der Behandlung von Leerzeichen nach dem hochgestellten und vor dem tiefgestellten Zeichen unterscheiden. Treffen Sie Ihre Wahl.

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

Bildbeschreibung hier eingeben

Noch ein paar weitere Informationen. Die Funktion \tl_range:nnnnimmt drei Argumente an, wobei das erste ein Text ist und das zweite und dritte ganze Zahlen, die den zu extrahierenden Bereich angeben. {1}{1}Extrahiert also das erste Element (es kann auch sein \tl_head:n, aber ich habe aus Gründen der Einheitlichkeit die komplexere Funktion verwendet), während {-2}{-1}die letzten beiden Elemente angegeben werden (bei negativen Indizes beginnt die Extraktion am Ende); {2}{-3}gibt den Bereich vom zweiten bis zum dritten Element beginnend von rechts an.

Um jedoch Leerzeichen an den Grenzen der extrahierten Teile beizubehalten, müssen wir Leerzeichen zunächst durch ersetzen \c_space_tl, was zu einem Leerzeichen erweitert wird, aber von den Extraktionsfunktionen nicht abgeschnitten wird. Die Syntax von \tl_set:Nnnist dieselbe, nur das erste Argument muss eine tl-Variable sein.

Antwort2

Der Komplexität halber zeige ich, wie dieses Problem auf der Ebene der TeX-Grundelemente gelöst werden kann:

\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

Antwort3

Der Abwechslung halber hier eine LuaLaTeX-basierte Lösung. Sie richtet eine Lua-Funktion ein, die wiederum Luas String-Funktionen string.subund verwendet, string.lenum ihre Aufgabe zu erfüllen. Sie richtet auch ein LaTeX-Wrapper-Makro namens ein \mynewcommand, das sein Argument einmal erweitert, bevor es an die Lua-Funktion übergeben wird.

Die Lösung verwendet tatsächlich Varianten der Lua-String-Funktionen unicode.utf8.subund unicode.utf8.len, um das Argument von\mynewcommand als jeden gültigen String aus UTF-8-kodierten Zeichen zuzulassen. (Umdruckendie Zeichen im String, eine passende Schriftart muss geladen werden.) Das Argument von \mynewcommandkann Primitive und Makros enthalten.

Bildbeschreibung hier eingeben

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

verwandte Informationen