
Ich versuche, viele verschiedene Befehle basierend auf einem einzigen Befehl (im Wesentlichen einem benutzerdefinierten Header) auf vorhersehbare Weise zu erstellen. Gibt es eine Möglichkeit, dies in TeX/LaTeX zu tun?
Mein konkretes Beispiel:
Ich schreibe eine Modulspezifikation, in der jede der Unterüberschriften mit \modulespec{description}{the description}
oder \modulespec{learning outcomes}{the learning outcomes}
usw. gekennzeichnet ist. Ich hoffe, dass TeX Befehle wie \mdescription{the description}
und generieren kann\mlearningoutcomes{the learning outcomes}
Ich weiß, dass ich in einer Bash-ähnlichen Erweiterung in Kombination mit TeX schreiben würde
for i in 'description' 'learning outcomes'
do
\def\csname m\getRidOfSpaces{$i} \endcsname{\modulespec{$i}{#1}}
done
(entschuldigen Sie die Sprachenkombination), wobei \getRidOfSpaces
sich ändert learning outcomes
in learningoutcomes
. Ist dies mit TeX/LaTeX möglich/durchführbar?
M(nicht)WIR:
\documentclass{article}
\usepackage{blindtext}
% In the real thing, this definition is much more complicated
\newcommand{\modulespec}[2]{\section{#1} #2}
% The code I want in pure TeX/LaTeX
for i in 'description' 'learning outcomes'
do
\def\csname m\getRidOfSpaces{$i} \endcsname{\modulespec{$i}{#1}}
done
% end of non TeX code
\begin{document}
% Both work
\modulespec{description}{\blindtext}
\modulespec{learning outcomes}{\blindtext}
% These two commands should produce the same output as the previous two
\mdescription{\blindtext}
\mlearningoutcomes{\blindtext}
\end{document}
Antwort1
Hier ist eine LaTeX3-Implementierung.
\documentclass{article}
\usepackage[margin=1cm]{geometry} % to fit in one page
\usepackage{blindtext}
\usepackage{xparse}
\newcommand{\modulespec}[2]{\section{#1} #2}
\ExplSyntaxOn
\NewDocumentCommand{\definemodules}{m}
{
\farley_definemodules:n { #1 }
}
\tl_new:N \l_farley_temp_tl
\cs_new_protected:Npn \farley_definemodules:n #1
{
\clist_map_inline:nn { #1 }
{
\tl_set:Nn \l_farley_temp_tl { ##1 }
\tl_replace_all:Nnn \l_farley_temp_tl { ~ } { }
\cs_new_protected:cpn { m \l_farley_temp_tl } { \modulespec { ##1 } }
}
}
\ExplSyntaxOff
\definemodules{
description,
learning outcomes
}
\begin{document}
\modulespec{description}{\blindtext}
\modulespec{learning outcomes}{\blindtext}
% These two commands should produce the same output as the previous two
\mdescription{\blindtext}
\mlearningoutcomes{\blindtext}
\end{document}
Einige Kommentare zum Code.
Wir definieren ein Makro auf Benutzerebene, um alle Module auf einmal zu definieren. Dadurch wird \definemodules
die Kontrolle einfach an die Funktion auf Programmiererebene übergeben \farley_definemodules:n
(dies ist die bewährte Vorgehensweise gemäß den LaTeX3-Programmierrichtlinien).
Jetzt teilt die Funktion \farley_definemodules:n
ihr Argument an Kommas auf und führt eine Schleife über die Elemente aus. Das aktuelle Element ist als verfügbar #1
, wir benötigen es jedoch ##1
, da wir uns innerhalb einer Definition befinden.
Jeder Zyklus speichert das Element in der Scratch-Token-Listenvariable, \l_farley_temp_tl
damit wir \tl_replace_all:Nnn
es anwenden können. Der Ersatz ist „jedes Leerzeichen wird durch nichts ersetzt“; in der Programmierumgebung werden Leerzeichen ignoriert, daher wird ein „echtes Leerzeichen“ durch gekennzeichnet ~
.
Nun verwenden wir die \cs_new_protected:cpn
Funktion, die das Analogon zum alten Stil ist \@namedef
: Das erste Argument wird in einen Steuersequenznamen umgewandelt \csname...\endcsname
(die Tokenliste wird hier gemäß der TeX-Regel erweitert).
Antwort2
Es sieht so aus, als ob Sie eine „for each“-Schleife ausführen möchten. Der LaTeX-Kernel verfügt über ein Makro \@for
, das dies für jedes Element einer durch Kommas getrennten Liste tut. Sie benötigen außerdem \zap@space
oder Ähnliches, um Leerzeichen zu entfernen. Beispiel:
\newcommand{\modulespec}[2]{\section{#1} #2}
\makeatletter
\@for\@tempa:=description,learning outcomes\do{%
\edef\@tempa
{\expandafter\expandafter\expandafter\zap@space
\expandafter\@tempa\space\@empty}%
\expandafter\edef\csname m\@tempa\endcsname
{\noexpand\modulespec{\@tempa}}%
}
\makeatother
\mdescription
wird und nach Bedarf definieren \mlearningoutcomes
. Ich habe ein verwendet, \edef
um die Erweiterung der Variable an den richtigen Stellen zu erzwingen. (Es hat \protected@edef
hier keinen Sinn, es anzuwenden, da der „Text“ darin „sicher“ sein muss \csname
.)
Antwort3
Verwenden \xintFor
der LaTeX2e-Kernel-Dienstprogramme (mit einem @
im Namen):
\documentclass{article}
\usepackage[english]{babel}
\usepackage{blindtext}
\usepackage{xinttools}
% In the real thing, this definition is much more complicated
\newcommand{\modulespec}[2]{\section{#1} #2}
% % The code I want in pure TeX/LaTeX
% for i in 'description' 'learning outcomes'
% do
% \def\csname m\getRidOfSpaces{$i} \endcsname{\modulespec{$i}{#1}}
% done
% % end of non TeX code
% Nota bene: in the above you probably meant \long\def, not \def
\makeatletter
\xintFor #1 in {description, learning outcomes}\do
{\long\@namedef{m\zap@space #1 \@empty}##1{\modulespec{#1}{##1}}}
\makeatother
\begin{document}
% Both work
\modulespec{description}{\blindtext}
\modulespec{learning outcomes}{\blindtext}
% These two commands should produce the same output as the previous two
\mdescription{\blindtext}
\mlearningoutcomes{\blindtext}
\end{document}