Überarbeitungsverlauf[Zurück]
Klicke auf Einblenden/Ausblenden von Überarbeitungen 2

13 Okt '18, 18:32

Skillmon's gravatar image

Skillmon
1.2k6

Also, wenn es nicht expandierbar sein muss, kannst du dir ein temporäres Macro definieren und damit testen: Zunächst brauchen wir eine Art, wie wir testen können, ob ein bestimmter Token in einer Zeichenfolge vorkommt, das Folgende implementiert einen solchen Test für den Token `a`: \def\tokentest@a #1a#2\endtokentest@a% {% \if\relax\detokenize{#2}\relax <no a contained> \else <a contained> \fi } % usage: \tokentest@a <string>a\endtokentest@a Der Test funktioniert folgendermaßen: Das Macro `\tokentest@a` liest in seinem ersten Argument alles, bis zu einem `a`, das zweite Argument ist alles bis zum `\endtokentest@a` (statt dieses (nicht definierten) Kontrollwortes kannst du auch jede beliebige andere, extrem unwahrscheinliche Zeichenfolge verwenden). Durch den Aufruf, in dem immer ein `a` direkt vor `\endtokentest@a` definiert wird, ist das zweite Argument leer, wenn das erste kein `a` enthält. Der Test `\if\relax\detokenize{#2}\relax` ist ein expandierbarer und sicherer Test auf ein leeres Argument. Nun brauchen wir das Ganze aber ein wenig dynamischer, da das erste Argument ja angeben soll, auf welche Zeichen geprüft wird. Also definieren wir uns ein Macro, das ein Testmacro wie oben definiert. Wir geben auch direkt vor, was in welchem Fall jeweils passieren soll: \newcommand\ifexists@define[1] {% \long\def\ifexists@test ##1#1##2\endifexists@test {% \if\relax\detokenize{##2}\relax \else \let\ifexists@if\iftrue \fi }% } Das Macro nimmt als Argument diejenige Zeichenfolge, auf die geprüft werden soll. Wir benötigen nur eine Aktion für den Fall, dass das Zeichen vorkommt, in welchem wir ein Hilfsmacro `\ifexists@if` zu `\iftrue` werden lassen (der andere Fall ist somit implizit abgedeckt, eben wenn `\ifexists@if` nicht `\iftrue` entspricht). Zusätzlich definieren wir uns noch ein Macro, das uns den Test ein wenig vereinfacht (wir benötigen es später, weil wir Argument 1 expandieren müssen): \newcommand\ifexists@test@helper[2] {% \ifexists@test#2#1\endifexists@test } Schließlich noch das eigentliche Macro, welches über die angegebenen gesuchten Zeichen iteriert und für jedes den Test durchführt. Hierfür verwende ich `\@for` aus dem LaTeX-Kernel. \long\def\ifexists #1 in #2% {% \begingroup \let\ifexists@if\iffalse \@for\zz:={#1}\do {% \expandafter\ifexists@define\expandafter{\zz}% \expandafter\ifexists@test@helper\expandafter{\zz}{#2}% }% \expandafter\ifexists@if \endgroup \expandafter\endgroup\ifexists@if \ignorespaces } Das Macro ignoriert Leerzeichen nach seinem zweiten Argument. Du kannst mit dem Macro nicht auf ein Komma im String prüfen, da sich das nicht mit der Syntax von `\@for` vertragen würde. Komplettes MWE: \documentclass[border=2mm]{standalone} \makeatletter \newcommand\ifexists{}% test whether there are conflicts \long\def\ifexists #1 in #2% {% \begingroup \let\ifexists@if\iffalse \@for\zz:={#1}\do {% \expandafter\ifexists@define\expandafter{\zz}% \expandafter\ifexists@test@helper\expandafter{\zz}{#2}% }% \expandafter\ifexists@if \endgroup \expandafter\endgroup\ifexists@if \ignorespaces } \newcommand\ifexists@define[1] {% \long\def\ifexists@test ##1#1##2\endifexists@test {% \if\relax\detokenize{##2}\relax \else \let\ifexists@if\iftrue \fi }% } \newcommand\ifexists@test@helper[2] {% \ifexists@test#2#1\endifexists@test } \makeatother \begin{document} \ifexists{a,e,f} in {abc} yep \else nope \fi \end{document} Soll immer auf die gleichen Token geprüft werden, ist obiges ohne Probleme auch expandierbar umsetzbar. Zum Vergleich der Aufwand bei Verwendung von `xparse` und `expl3` (keine Schleife, sondern nur ein Test, ob #1 in #2 vorkommt): \documentclass[border=2mm]{standalone} \usepackage{xparse} \ExplSyntaxOn \NewDocumentCommand \ifexists { +m +m } { \tl_if_in:nnTF { #2 } { #1 } } \ExplSyntaxOff \begin{document} \ifexists{a}{abc} {yep} {nope} \end{document}
Klicke auf Einblenden/Ausblenden von Überarbeitungen 1

13 Okt '18, 00:09

Skillmon's gravatar image

Skillmon
1.2k6

Also, wenn es nicht expandierbar sein muss, kannst du dir ein temporäres Macro definieren und damit testen: Zunächst brauchen wir eine Art, wie wir testen können, ob ein bestimmter Token in einer Zeichenfolge vorkommt, das Folgende implementiert einen solchen Test für den Token `a`: \def\tokentest@a #1a#2\endtokentest@a% {% \if\relax\detokenize{#2}\relax <no a contained> \else <a contained> \fi } % usage: \tokentest@a <string>a\endtokentest@a Der Test funktioniert folgendermaßen: Das Macro `\tokentest@a` liest in seinem ersten Argument alles, bis zu einem `a`, das zweite Argument ist alles bis zum `\endtokentest@a` (statt dieses (nicht definierten) Kontrollwortes kannst du auch jede beliebige andere, extrem unwahrscheinliche Zeichenfolge verwenden). Durch den Aufruf, in dem immer ein `a` direkt vor `\endtokentest@a` definiert wird, ist das zweite Argument leer, wenn das erste kein `a` enthält. Der Test `\if\relax\detokenize{#2}\relax` ist ein expandierbarer und sicherer Test auf ein leeres Argument. Nun brauchen wir das Ganze aber ein wenig dynamischer, da das erste Argument ja angeben soll, auf welche Zeichen geprüft wird. Also definieren wir uns ein Macro, das ein Testmacro wie oben definiert. Wir geben auch direkt vor, was in welchem Fall jeweils passieren soll: \newcommand\ifexists@define[1] {% \long\def\ifexists@test ##1#1##2\endifexists@test {% \if\relax\detokenize{##2}\relax \else \let\ifexists@if\iftrue \fi }% } Das Macro nimmt als Argument diejenige Zeichenfolge, auf die geprüft werden soll. Wir benötigen nur eine Aktion für den Fall, dass das Zeichen vorkommt, in welchem wir ein Hilfsmacro `\ifexists@if` zu `\iftrue` werden lassen (der andere Fall ist somit implizit abgedeckt, eben wenn `\ifexists@if` nicht `\iftrue` entspricht). Zusätzlich definieren wir uns noch ein Macro, das uns den Test ein wenig vereinfacht (wir benötigen es später, weil wir Argument 1 expandieren müssen): \newcommand\ifexists@test@helper[2] {% \ifexists@test#2#1\endifexists@test } Schließlich noch das eigentliche Macro, welches über die angegebenen gesuchten Zeichen iteriert und für jedes den Test durchführt. Hierfür verwende ich `\@for` aus dem LaTeX-Kernel. \long\def\ifexists #1 in #2% {% \begingroup \let\ifexists@if\iffalse \@for\zz:={#1}\do {% \expandafter\ifexists@define\expandafter{\zz}% \expandafter\ifexists@test@helper\expandafter{\zz}{#2}% }% \expandafter\ifexists@if \endgroup \ignorespaces } Das Macro ignoriert Leerzeichen nach seinem zweiten Argument. Du kannst mit dem Macro nicht auf ein Komma im String prüfen, da sich das nicht mit der Syntax von `\@for` vertragen würde. Komplettes MWE: \documentclass[border=2mm]{standalone} \makeatletter \newcommand\ifexists{}% test whether there are conflicts \long\def\ifexists #1 in #2% {% \begingroup \let\ifexists@if\iffalse \@for\zz:={#1}\do {% \expandafter\ifexists@define\expandafter{\zz}% \expandafter\ifexists@test@helper\expandafter{\zz}{#2}% }% \expandafter\ifexists@if \endgroup \ignorespaces } \newcommand\ifexists@define[1] {% \long\def\ifexists@test ##1#1##2\endifexists@test {% \if\relax\detokenize{##2}\relax \else \let\ifexists@if\iftrue \fi }% } \newcommand\ifexists@test@helper[2] {% \ifexists@test#2#1\endifexists@test } \makeatother \begin{document} \ifexists{a,e,f} in {abc} yep \else nope \fi \end{document} Soll immer auf die gleichen Token geprüft werden, ist obiges ohne Probleme auch expandierbar umsetzbar. Zum Vergleich der Aufwand bei Verwendung von `xparse` und `expl3` (keine Schleife, sondern nur ein Test, ob #1 in #2 vorkommt): \documentclass[border=2mm]{standalone} \usepackage{xparse} \ExplSyntaxOn \NewDocumentCommand \ifexists { +m +m } { \tl_if_in:nnTF { #2 } { #1 } } \ExplSyntaxOff \begin{document} \ifexists{a}{abc} {yep} {nope} \end{document}

Willkommen, erstes Mal hier? Schau mal unter FAQ!

×