\ifstrequal funktioniert nicht mit pgfkeys

Redefinition von Makros, Definition eigener Befehle sowie neuer Umgebungen


Achim123
Forum-Newbie
Forum-Newbie
Beiträge: 4
Registriert: Do 31. Jan 2019, 09:00

\ifstrequal funktioniert nicht mit pgfkeys

Beitrag von Achim123 »

Hallo,

ich habe ein Problem und konnte es leider trotz einiger Lektüre nicht lösen. Ich möchte gerne Variablen in einer Style-Datei verwalten können. Im tatsächlichen LaTeX-File möchte ich die Variable ändern können, und je nach Wert der Variable, z.B. true oder false, soll mir ein Getter, z.B. \getvalue, etwas anderes zurückgeben.

Ich habe da mal ein Minimalbeispiel vorbereitet, das sich mit Lualatex (TeX Live 2016/Debian) kompilieren lassen sollte:
\documentclass{report}

\usepackage{pgfkeys}
\RequirePackage{pgfopts}
\RequirePackage{xcolor}
\RequirePackage{parselines}
\RequirePackage{filecontents}
\usepackage{etoolbox}

%%%%%%%%%%%%%%%%%%%%%
%%      GETTER UND SETTER
\newcommand{\setvalue}[1]{\pgfkeys{/variables/#1}}
\newcommand{\getvalue}[1]{%
        \ifstrequal{\pgfkeysvalueof{/variables/#1}}{true}{%
                true}{false%
        }

        In der Variable steht aber:
        \pgfkeysvalueof{/variables/#1}
}
\newcommand{\declare}[1]{%
\pgfkeys{
        /variables/#1.is family,
        /variables/#1.unknown/.style = {\pgfkeyscurrentpath/\pgfkeyscurrentname/.initial = ##1}
        }%
}

\declare{}


%%%%%%%%%%%%%%%%%
%% BEGIN DOCUMENT
\begin{document}
\setvalue{VARIABLE1 = true}

Die Auswertung ergibt:
\getvalue{VARIABLE1} % schreibt immer "false"

\declare{test/}

\end{document}
Meine Vermutung ist ja, dass es was mit expandafter zu tun hat, aber ... hm ... bin einfach noch nicht dahinter gestiegen, was es sein könnte :-/

Habt ihr eine Idee?

Gast

Beitrag von Gast »

Nun, \ifstrequal expandiert laut Anleitung die Argumente, die es vergleicht nicht, sondern vergleicht direkt die Zeichenkette. Und dass eine Zeichenkette, die \pgfkeysvalueof] enthält niemals mit der Zeichenkette true identisch sein kann, sollte nicht soooo wahnsinnig verwunden. Du kannst aber stattdessen erst einmal ein Makro mit \pgfkeysgetvalue definieren und dann mit Hilfe von \ifcsstring mit true vergleichen:
\documentclass{report}

\usepackage{pgfkeys}
\RequirePackage{pgfopts}
\RequirePackage{xcolor}
\RequirePackage{parselines}
\RequirePackage{filecontents}
\usepackage{etoolbox}

%%%%%%%%%%%%%%%%%%%%%
%%      GETTER UND SETTER
\newcommand*{\tempvar}{}% Fehlermeldung, falls schon jemand \tempvar verwendet.
\newcommand{\setvalue}[1]{\pgfkeys{/variables/#1}}
\newcommand{\getvalue}[1]{%
  \pgfkeysgetvalue{/variables/#1}{\tempvar}
  \ifcsstring{tempvar}{true}{%
                true}{false%
        }

        In der Variable steht:
        \pgfkeysvalueof{/variables/#1}
}
\newcommand{\declare}[1]{%
\pgfkeys{
        /variables/#1.is family,
        /variables/#1.unknown/.style = {\pgfkeyscurrentpath/\pgfkeyscurrentname/.initial = ##1}
        }%
}

\declare{}


%%%%%%%%%%%%%%%%%
%% BEGIN DOCUMENT
\begin{document}
\setvalue{VARIABLE1 = true}

Die Auswertung ergibt:
\getvalue{VARIABLE1} % schreibt immer "false"

\declare{test/}

\end{document}
Wobei mir der Sinn von \getvalue nicht wirklich klar ist. Daher sei noch darauf hingewiesen, dass pgfkeys für boolsche Variablen spezielle Routinen bereitstellt.

Gast

Beitrag von Gast »

Das ist in der Tat eine Expansions-Frage. \ifstrequal geht davon aus, dass es zwei Strings als Argument bekommt. In dem Fall hier bekommt es aber ein Argument, das nach drei Expansionsschritten ein String wird (wenn ich richtig gezählt habe) und einen String. Du müsstest \pgfkeysvalueof{/variables/#1} drei mal expandieren, um an den String zu kommen. Das braucht dann zwei mal sieben \expandafters. Stattdessen kannst Du aber auch mit einer Hilfsvariable und \edef/\expandthrice arbeiten.
\documentclass{article}

\usepackage{pgfkeys}
\RequirePackage{pgfopts}
\usepackage{etoolbox}

% wie \expandonce von etoolbox, nur dreimal
\newcommand\expandthrice{%
  \unexpanded\expandafter\expandafter\expandafter
  \expandafter\expandafter\expandafter\expandafter}

\newcommand{\setvalue}[1]{\pgfkeys{/variables/#1}}
\newcommand{\getvalue}[1]{%
  \expandafter\expandafter\expandafter\expandafter\expandafter
  \expandafter\expandafter
  \ifstrequal
  \expandafter\expandafter\expandafter\expandafter\expandafter
  \expandafter\expandafter
  {\pgfkeysvalueof{/variables/#1}}{true}
    {true}
    {false}%

  \edef\getvaltmp{\expandthrice{\pgfkeysvalueof{/variables/#1}}}%
  \ifdefstring{\getvaltmp}{true}
    {true}
    {false}%

  In der Variable steht aber:
  \pgfkeysvalueof{/variables/#1}
}
\newcommand{\declare}[1]{%
  \pgfkeys{
    /variables/#1.is family,
    /variables/#1.unknown/.style = {\pgfkeyscurrentpath/\pgfkeyscurrentname/.initial = ##1}
  }%
}

\declare{}


\begin{document}
\setvalue{VARIABLE1 = true}

Die Auswertung ergibt:
\getvalue{VARIABLE1}

\declare{test/}
\end{document}
Je nach echter Anwedung gibt es da vielleicht deutlich bessere Methoden. Und vielleicht kann auch pgfkeys selbst noch was Besseres (da bin ich aber überfragt).

Achim123
Forum-Newbie
Forum-Newbie
Beiträge: 4
Registriert: Do 31. Jan 2019, 09:00

Beitrag von Achim123 »

Was ... um alles in der Welt ... *sieben* \expandafter, zweimal?

Ok, mega krass ... wie bist du da drauf gekommen, dass das so gehen könnte? (noch nicht getestet) Ich möchte ja für die Zukunft lernen und nächstes Mal selbst draufkommen oder es selbst recherchieren können

Vielen Dank schonmal!

Gast

Beitrag von Gast »

So etwas braucht entweder intime Kenntnisse über die Definition oder Ausprobieren.

Ich würde aber stattdessen empfehlen, auf derartige interne Kenntnisse zu verzichten. Solche Hackerlösungen gehen schon einmal kaputt, was üblicherweise bei der Verwendung dafür vorgesehener Schnittstellen eines Pakets nicht passiert (oder dann ggf. als Bug gemeldet werden kann).

Achim123
Forum-Newbie
Forum-Newbie
Beiträge: 4
Registriert: Do 31. Jan 2019, 09:00

Beitrag von Achim123 »

Hm, ok. Gibt es denn eine andere Möglichkeit, eine Variable auf Gleichheit mit einem String zu testen als \ifstrequal ? Wie machen andere das in LaTeX, wenn sie wissen wollen, was in einer Variablen drinsteht?

Gast

Beitrag von Gast »

Wurde doch oben gezeigt und auch in meinem letzten Kommentar gleich zweimal verlinkt. Nur weil zwei Gäste zwei unterschiedliche Antworten gegeben haben, ist das doch kein Grund die erste komplett zu ignorieren …

Gast

Beitrag von Gast »

Das kommt ganz auf die Aufgabenstellung an. Wenn ich nur wissen will, was in einer Variablen drin steht, kann ich sie mir ja einfach ausgeben lassen. Für die interne Verwendung in Paketen gibt es häufig andere Methoden als mit Strings zu arbeiten, sodass man sich die recht komplexen Stringvergleiche sparen kann. Bei Boolean-Werten bin ich zum Beispiel ein Freund von toggles aus etoolbox, aber es gibt viele ähnliche und andere Methoden. Wenn es um die Ausgabe von irgendwas geht, dann ist es häufig einfacher, die Ausgabe von Anfang an so auszulegen, dass man den String einfach ausgibt, anstatt ihn noch aufwendig testen zu müssen und dann zu reagieren.

Die um 19:03 gezeigte Lösung nutzt statt des Expandierballets mit \edef oder \expandafter eben direkt eine Schnittstelle von pgfkeys und ist daher nicht nur einfacher zu verstehen sondern, wie erwähnt, auch sicherer. (Gibt es einen Grund, warum Du \ifcsstring verwendet hast? Ich hätte hier \ifdefstring{\tempvar}{true} wegen der Verwendung von \tempvar in \pgfkeysgetvalue{/variables/#1}{\tempvar} fast klarer gefunden.)

Den ganzen Lösungen hier liegt aber zugrunde, dass man zwischen einer Variablen (einem Makro) und ihrem Wert (seiner Expansion) unterscheiden muss.

Achim123
Forum-Newbie
Forum-Newbie
Beiträge: 4
Registriert: Do 31. Jan 2019, 09:00

Beitrag von Achim123 »

Oh, ok, ja, ich hatte die erste Lösung überlesen :-( Sorry!

Vielen lieben Dank. Ich glaube, ich komme damit erstmal weiter. Habe auch \ifcsstring verwendet und mit \edef\tempvar{} direkt in \getvalue geschrieben. Scheint mir aufgeräumter(?)

Gast

Beitrag von Gast »

Mhhh, ich dachte wir waren uns alle einig, dass
\pgfkeysgetvalue{/variables/#1}{\tempvar}
die bessere Variante war? \edef\getvaltmp{\expandthrice{\pgfkeysvalueof{/variables/#1}}}% geht zwar auch, ist aber von der Implementation abhängig und nutzt eben nicht die extra bereitgestellten Schnittstellen. Daher schrieb ich ja auch: "Je nach echter Anwendung gibt es da vielleicht deutlich bessere Methoden. Und vielleicht kann auch pgfkeys selbst noch was Besseres (da bin ich aber überfragt)." (Das Timing war natürlich etwas unglücklich. Als ich meine Antwort schrieb, war die von 19:03 noch nicht da. Aber da ich so lange getippt habe, war sie später fertig als die Antwort von 19:03, die ja eine passende Schnittstelle demonstriert.)

Mit der \pgfkeysgetvalue-Methode ist, soweit ich sehen kann, gar kein \edef erforderlich (und das ist auch gut, denn \edefs können manchmal unerwartete Effekte haben, wenn man nicht genau aufpasst).

Antworten