符號函數:更改catcodes和\tl_rescan:nn

符號函數:更改catcodes和\tl_rescan:nn

目標是讓使用者輸入(為了論證)一個變數中的線性函數,例如2*x+3變數(這並不重要)和變數的值。然後我想計算該函數的值。以下內容有效,但我現在認為它的工作更多是出於偶然而不是其他任何事情。

  1. 我最初的嘗試\findvalueB失敗了,而且似乎不喜歡下劃線,所以我嘗試了\findvalueA其中有效的方法。為什麼其中一個有效而另一個無效?

  2. 當決定切換變數名稱進行測試時,我遇到了明顯的問題(例如使用y中斷)\mytoks。 所以我嘗試對變數名稱進行硬編碼,再次失敗。

據推測,我沒有得到的東西有一個共同點,這可能是對我想糾正的事情的根本誤解。我對解釋比代碼解決方案更感興趣,儘管兩者都很好。

\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}

答案1

方法 B 的失敗很容易解釋。重新掃描材料會讀取目前適用的類別代碼。當你使用 \findvalueB,您在文件中,因此有類別“數學下標”,而不是“字母”(稍後_您還會發現這是錯誤的)。:人們常常對此感到驚訝,因為他們想像重新掃描將使用定義命令時設定的程式碼。這是使重新掃描變得非常棘手的一件事。

方法C的失敗更難修復。這裡發生的情況是,TeX\defx在執行重新掃描之後「有用地」插入一個空格,就像在更改標記化時在任何其他控制序列之後(例如 in\write\detokenize)一樣。這發生在重新掃描“之前”,其中\defx將是單一令牌。結果是,在方法 C 中x被定義為後面跟著一個必要的空格:沒有空格,所以我們得到錯誤。在方法 A 中不會發生這種情況,因為此處的空格是在重新掃描之前\def和之前插入的#1(它們是重新掃描之前的單獨標記)。因此這個方法會起作用。

這兩個問題都說明了為什麼重新掃描不受許多 TeX 程式設計師的青睞:我傾向於避免它。我們在 LaTeX3 中提供了合理的包裝器,但是您的問題表明要正確使用它仍然很棘手。您真正想要的是將所有變數字母替換為值。因此,我根本不會重新掃描,而是會使用

\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 }
  }

不存在任何這些問題。 (我假設您沒有sin在表達式和字母中n作為可能的變數:可以處理這種情況,但需要更多思考!)

相關內容