Symbolische Funktionen: Ändern von Catcodes und \tl_rescan:nn

Symbolische Funktionen: Ändern von Catcodes und \tl_rescan:nn

Das Ziel ist, dass der Benutzer (um der Argumentation willen) eine lineare Funktion in einer Variablen eingibt, z. B. 2*x+3zusammen mit der Variablen (das ist nicht entscheidend) und einem Wert für die Variable. Ich möchte dann den Wert der Funktion berechnen. Das Folgende funktioniert, aber ich denke jetzt, dass es eher zufällig als aus irgendeinem anderen Grund funktioniert.

  1. Mein erster Versuch \findvalueBschlug fehl, und mir gefiel der Unterstrich nicht, also probierte ich die Methode aus, \findvalueAdie funktionierte. Warum funktioniert eine davon und die andere nicht?

  2. Als ich mich entschied, die Variablennamen zu Testzwecken zu ändern, stieß ich auf die offensichtlichen Probleme (z. B. die Verwendung von y„breaks“) \mytoks. Also versuchte ich, einen Variablennamen fest zu codieren, was wieder fehlschlug. Die Grundidee scheint zu funktionieren, wie unten gezeigt. Warum schlägt dieser Versuch fehl?

Vermutlich gibt es einen roten Faden bei dem, was ich nicht verstehe, und es handelt sich wahrscheinlich um ein grundlegendes Missverständnis von etwas, das ich gerne korrigieren würde. Mich interessiert mehr die Erklärung als eine Code-Lösung, obwohl beides nett wäre.

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\newtoks\mytoks

\NewDocumentCommand {\findvalueA} { m m m }
    {
        \group_begin:
        \char_set_catcode_active:N #1
        \tl_rescan:nn{}{\def#1{#2}}
        \tl_rescan:nn{}{\mytoks={#3}}
        \fp_set:Nn \l_tmpa_fp {\the\mytoks}
        \fp_use:N \l_tmpa_fp
        \group_end:
    }

\NewDocumentCommand {\findvalueB} { m m m }
    {
        \group_begin:
        \char_set_catcode_active:N #1
        \tl_rescan:nn{}{\def#1{#2}}
        % underscores=bad
        \tl_rescan:nn{}{\tl_set:Nn \l_tmpa_tl {#3}}
        % or
        % \tl_rescan:nn{}{\fp_eval:n {#3}}
        \fp_use:N \l_tmpa_fp
        \group_end:
    }

    \NewDocumentCommand {\findvalueC} { m m }
    {
        \group_begin:
        \char_set_catcode_active:N x
        \tl_rescan:nn {} {\defx{#1}}
        \tl_rescan:nn {} {\mytoks={#2}}
        \fp_set:Nn \l_tmpa_fp {\the\mytoks}
        \fp_use:N \l_tmpa_fp
        \group_end:
    }

\ExplSyntaxOff
\begin{document}

% works
\findvalueA{x}{5}{2*x+3}

% fails
%\findvalueB{x}{5}{2*x+3}

% fails
%\findvalueC{5}{x+2}

The method of findvalueC works down here but not above: if $x=5$, then $x+2=$
\ExplSyntaxOn
\group_begin:
\char_set_catcode_active:N x
\defx{5}
\fp_eval:n {x+2}
\group_end:
\ExplSyntaxOff

\end{document}

Antwort1

Das Versagen von Methode B ist recht einfach zu erklären. Beim erneuten Scannen wird das Material mit den aktuell gültigen Kategoriecodes gelesen. Wenn Sieverwenden \findvalueB, Sie befinden sich im Dokument und _haben daher die Kategorie „mathematisches Subskript“, nicht „Buchstabe“ (Sie werden auch feststellen, dass das :falsch ist, etwas später). Die Leute sind davon oft überrascht, da sie davon ausgehen, dass beim erneuten Scannen die Codes verwendet werden, die beim Definieren des Befehls festgelegt wurden. Dies ist einer der Gründe, warum das erneute Scannen ziemlich schwierig ist.

Der Fehler bei Methode C ist schwieriger zu erkennen. Was hier passiert, ist, dass TeX „hilfreicherweise“ ein Leerzeichen nach \defxdem erneuten Scan einfügt, genauso wie es das nach jeder anderen Steuersequenz tut, wenn die Tokenisierung geändert wird (zum Beispiel in \writeoder \detokenize). Dies geschieht „vor“ dem erneuten Scan, wobei \defxein einzelnes Token steht. Das Ergebnis ist, dass in Methode C xdefiniert ist, dass darauf ein erforderliches Leerzeichen folgt: Es gibt kein Leerzeichen, also erhalten wir den Fehler. Das passiert bei Methode A nicht, da das Leerzeichen hier nach \defund vor eingefügt wird #1(es sind separate Token vor dem erneuten Scan). Daher wird diese Methode funktionieren.

Beide Probleme zeigen, warum viele TeX-Programmierer das erneute Scannen nicht bevorzugen: Ich vermeide es eher. Wir haben in LaTeX3 vernünftige Wrapper bereitgestellt, aber Ihre Probleme zeigen, dass es immer noch schwierig ist, es richtig zu machen. Was Sie eigentlich wollen, ist, alle Variablenbuchstaben durch den Wert zu ersetzen. Ich würde daher überhaupt nicht erneut scannen, sondern verwenden

\NewDocumentCommand { \findvalueD } { m m m }
  {
    \tl_set:Nn \l_tmpa_tl {#3}
    \tl_replace_all:Nnn \l_tmpa_tl {#1} {#2}
    \fp_eval:n { \l_tmpa_tl }
  }

das keines dieser Probleme hat. (Ich gehe davon aus, dass Sie beispielsweise sinim Ausdruck nicht den Buchstaben nals mögliche Variable haben: Ein solcher Fall könnte behandelt werden, würde aber mehr Überlegung erfordern!)

verwandte Informationen