目標は、ユーザーに (議論のために) 1 つの変数の線形関数を入力させることです。たとえば、2*x+3
変数 (これは重要ではありません) と変数の値と一緒に入力します。次に、関数の値を計算します。次のコードは機能しますが、今のところ、他の何よりも偶然に機能しているのではないかと考えています。
最初の試みは
\findvalueB
失敗し、アンダースコアが気に入らなかったようだったので、\findvalueA
うまくいった方法を試しました。なぜこれらのうちの 1 つは機能し、もう 1 つは機能しないのでしょうか?テストするために変数名を切り替えることにしたとき、明らかな問題に遭遇しました (例:
y
breaks の使用\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
、あなたは文書内にいるので、_
カテゴリは「文字」ではなく「数学下付き文字」です (:
少し後で、これも間違っていることがわかります)。コマンドが定義されたときに設定されたコードが再スキャンで使用されると想像しているため、これに驚く人はよくいます。これは、再スキャンの使用を非常にトリッキーにする 1 つの要因です。
方法 C の失敗は、見つけるのがより困難です。ここで起きていることは、TeX が\defx
再スキャンを行うときに、他の制御シーケンスの後にトークン化を変更するときと同じように、スペースを「適切に」挿入することです (たとえば、または\write
) \detokenize
。これは再スキャンの「前」に発生し、 は\defx
単一のトークンになります。その結果、方法 C では、 の後に必須のスペースが続くと定義されます。スペースがないため、エラーが発生します。方法 A では、スペースが前後x
に挿入されるため(再スキャン前は別々のトークン)、これは発生しません。したがって、この方法は機能します。\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
変数として含まれていないと仮定しています。このようなケースは処理できますが、さらに検討する必要があります。)