Verallgemeinerung eines Makros zweiter Ordnung mit Schleifen

Verallgemeinerung eines Makros zweiter Ordnung mit Schleifen

Ich möchte ein Makro konstruieren, mit dem man Befehle definieren kann. Diese Befehle verhalten sich wie Variablen oder Variablenstrukturen, sodass sie mehrere Werte enthalten können. Das „Mitglied“ wird über ein optionales Argument übergeben. Ich verwende es, um Vorlagenumgebungen zu definieren und unterschiedliche Zeichenfolgen für unterschiedliche Sprachen zu haben, die gleichzeitig im Dokument vorhanden sein müssen.

Hier ist, was ich hatte und was funktioniert.

\newcommand\MakeLocaleVar[1]{%
    \global\@namedef{#1:en}{{\scriptsize (Use {\tt\textbackslash #1[en]} to replace this text.)}}%
    \global\@namedef{#1:fi}{{\scriptsize (Use {\tt\textbackslash #1[fi]} to replace this text.)}}%
    \expandafter\newcommand\csname #1\endcsname[2][]{%
        \ifthenelse{\equal{##1}{}}{%
            \global\@namedef{#1:en}{##2}%
            \global\@namedef{#1:fi}{##2}%
        }{%
            \global\@namedef{#1:##1}{##2}%
        }%
    }%
    \expandafter\newcommand\csname Emit#1\endcsname[1][en]{\@nameuse{#1:##1}}%
}

Zuerst werden die Standardwerte festgelegt. Dann wird ein Befehl erstellt, der die Werte gemäß dem optionalen Argument festlegt. Wenn keines vorhanden ist, wird der Wert für alle Gebietsschemas festgelegt.

% In cls: define command
\MakeLocaleVar{Faculty}

% In main tex: set values
\Faculty{This faculty} % for all values
\Faculty[fi]{Tämä tiedekunta} % for a specific one

% In cls environments: use values
\EmitFaculty[en]
\EmitFaculty[fi]

% Now in addition I'd like to be able to:
\MakeLocaleVar[en,fi,de]{Faculty}

Ich habe versucht, den Befehl so zu ändern, dass beliebige Gebietsschemas akzeptiert werden, aber etwas funktioniert nicht.

\newcommand\MakeLocaleVar[2][en,fi]{%
    \foreach \n in {#1}{%
        \global\@namedef{#2:\n}{%
            {\scriptsize (Use {\tt\textbackslash #2[\n]} to replace this text.)}%
        }%
    }%
    \expandafter\newcommand\csname #2\endcsname[2][]{%
        \ifthenelse{\equal{##1}{}}{%
            \foreach \n in {#1}{%
                \global\@namedef{#2:\n}{##2}%
            }%
        }{%
            \global\@namedef{#2:##1}{##2}%
        }%
    }%
    \expandafter\newcommand\csname Emit#2\endcsname[1][en]{\@nameuse{#2:##1}}%
}

Wenn ich die verwendeten Werte einstelle, ist alles in Ordnung. Wenn keine Werte festgelegt sind, funktionieren meine benutzerdefinierten Umgebungen nicht mehr und die angezeigte Standardmeldung lautet nur Use \Cmd[] to..., also ohne die Gebietsschemanamen.

Irgendeine Idee, was los ist?

Antwort1

Wie im Kommentar erwähnt, war Ihr Hauptproblem, dass das \nim Ersetzungstext von \@namedefnicht auf seinen Wert erweitert wurde. Der Ersetzungstext für Faculty:fibeispielsweise blieb also wörtlich

{\scriptsize (Use {\tt\textbackslash #2[\n]} to replace this text.)}%

mit \n. In den meisten Kontexten, in denen das Makro aufgerufen würde, \nwäre es nicht definiert und Sie würden eine Fehlermeldung erhalten. Sie möchten, dass der Makro-Ersetzungstext so erstellt wird, dass \nseine Erweiterung wird, also fioder en. Der einfachste Weg, dies richtig zu erledigen, ist wahrscheinlich ein Hilfsmakro. Diese Antwort zeigt zwei Ansätze: Einen mit etoolboxund seinen Listenmakros und einen mit Ihrem Code, der \expandafterund ein Hilfsmakro verwendet. etoolboxDie Schleifen von funktionieren, indem der Wert der Schleifenvariable direkt als Argument an ein Hilfsmakro übergeben wird.

Im ersten Argument von \@namedef(also dem #2:\n) \nwürde das automatisch erweitert, so dass es auch wirklich dort stünde fi.


Hier ist eine Lösung, die etoolboxund ihre Listenmakros verwendet. Einige Erklärungen sind inline. Der Vorteil dieses Ansatzes besteht darin, dass keine Erweiterung der Schleifenvariable ( \n) erforderlich ist, da die Schleife direkt als Makro implementiert ist (normalerweise ist ein Hilfsmakro erforderlich).

\documentclass[english,finnish]{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{babel}

\usepackage{etoolbox}

\makeatletter
% {<name>}{<lang>}
\newcommand*{\mlv@defaultvalue}[2]{%
  \csdef{#1@#2}{%
    {\scriptsize (Use {\ttfamily\textbackslash #1[#2]} to replace this text.)}}}
% {<repl. text>}{<name>}{<lang>}
% the unusual order instead of the more natural {<name>}{<lang>}{<repl. text>}
% makes for a more elegant call to \forcsvlist
\newcommand*{\mlv@realvalue}[3]{%
  \csdef{#2@#3}{#1}}

% [<langs>]{<name>}
% \forcsvlist{<macro>}{<item_1, item_2, ..., item_n>}
% calls <macro> once with each item_i as last argument
% <macro>{<item_1>}, <macro>{<item_2>}
% <macro> can already bring a few arguments of its own
% it could be <marco>{<fixed_1>}...{<fixed_n>} and then the invocations
% would become <marco>{<fixed_1>}...{<fixed_n>}{<item_1>} etc.
% Since the items are the <lang> argument it must be the last
% argument to \mlv@defaultvalue and \mlv@realvalue
\newcommand\MakeLocaleVar[2][en,fi]{%
  \forcsvlist{\mlv@defaultvalue{#2}}{#1}%
  \expandafter\newcommand\csname #2\endcsname[2][]{%
    \ifblank{##1}
      {\forcsvlist{\mlv@realvalue{##2}{#2}}{#1}}%
      {\mlv@realvalue{##2}{#2}{##1}}}%
  \expandafter\newcommand\csname Emit#2\endcsname[1][en]{\csuse{#2@##1}}%
}
\makeatother

\begin{document}

\MakeLocaleVar{Faculty}

% In main tex: set values
\Faculty{This faculty} % for all values
\Faculty[fi]{Tämä tiedekunta} % for a specific one

% In cls environments: use values
\EmitFaculty[en]
\EmitFaculty[fi]

% Now in addition I'd like to be able to:
\MakeLocaleVar[en,fi,de]{Gaculty}

\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]

\Gaculty{Foo}
\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]

\Gaculty[fi]{Föö}
\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]
\end{document}

Diese Fakultät ist nicht erreichbar//(Verwenden Sie \Gaculty[en], um diesen Text zu ersetzen.) (Verwenden Sie \Gaculty[fi], um diesen Text zu ersetzen.) (Verwenden Sie \Gaculty[de], um diesen Text zu ersetzen.)//Foo Foo Foo//Foo Föö Foo


\foreachWenn Sie bei Ihrer Version mit und bleiben möchten \ifthenelse, können Sie eine Hilfsfunktion verwenden.

Der Trick besteht darin, dass wir erweitern müssen, \ndamit wir den tatsächlichen Wert erhalten. Dies ist mit möglich \expandafter, aber um nicht mit chirurgischer Präzision viele Token überspringen zu müssen, ist eine Hilfsfunktion nützlich. Da es etwas einfacher ist, das erste Argument eines Makros mit s statt mit einem späteren Argument zu erweitern \expandafter, verwendet die Hilfsfunktion die etwas unerwartete Argumentreihenfolge {<lang>}{<name>}.

\documentclass[english,finnish]{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{babel}

\usepackage{ifthen}
\usepackage{tikz}

\makeatletter
% helper for easier control of expansion
% {<lang>}{<name>}
\newcommand*{\mlv@helper}[2]{%
  \global\@namedef{#2:#1}{%
    {\scriptsize (Use {\ttfamily\textbackslash #2[#1]} to replace this text.)}%
  }%
}

% \n must be expanded to be useful.
% The first argument of \@namedef automatically expands it,
% but the second does not.
% Here we use a helper function to expand \n
% before it is processed.
\newcommand\MakeLocaleVar[2][en,fi]{%
    \foreach \n in {#1}{%
        \expandafter\mlv@helper\expandafter{\n}{#2}%
    }%
    \expandafter\newcommand\csname #2\endcsname[2][]{%
        \ifthenelse{\equal{##1}{}}{%
            \foreach \n in {#1}{%
                \global\@namedef{#2:\n}{##2}%
            }%
        }{%
            \global\@namedef{#2:##1}{##2}%
        }%
    }%
    \expandafter\newcommand\csname Emit#2\endcsname[1][en]{\@nameuse{#2:##1}}%
}
\makeatother

\begin{document}

\MakeLocaleVar{Faculty}

% In main tex: set values
\Faculty{This faculty} % for all values
\Faculty[fi]{Tämä tiedekunta} % for a specific one

% In cls environments: use values
\EmitFaculty[en]
\EmitFaculty[fi]

% Now in addition I'd like to be able to:
\MakeLocaleVar[en,fi,de]{Gaculty}

\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]

\Gaculty{Foo}
\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]

\Gaculty[fi]{Föö}
\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]
\end{document}

Übrigens: Ich habe \ttfamilyanstelle von verwendet \tt, da die zweistelligen Schriftbefehle in LaTeX2e veraltet sind (https://texfaq.org/FAQ-2letterfontcmd,Werden zweibuchstabige Schriftstilbefehle (\bf, \it, …) jemals in LaTeX wiederbelebt?).

verwandte Informationen