マクロは数式モードでのみ定義されます

マクロは数式モードでのみ定義されます

私はよく数式で頻繁に使用する記号、例えば\d x微分dxや\vベクトルが必要な場合などに短いマクロを使用します。読みやすくするために、テキストで頻繁に を使用します。ただし、 は\d次の文字の下のドットとvハチェク アクセントを表すため、定義済みマクロと衝突します。これらは上書きしたくありません (参考文献で必要になるかもしれません...)。

そこで、数式モードでのみこれらのマクロを上書きするために、次の方法を思いつきました。

\documentclass{article}

\usepackage{everyhook}
\newcommand{\mathdef}[2]{%
  \PushPostHook{math}{\expandafter\def\csname #1\endcsname{#2}}%
  \PushPostHook{display}{\expandafter\def\csname #1\endcsname{#2}}}
\mathdef{d}{\mathrm{d}}%

\begin{document}
  \d x  is an x with a dot below and $\int f(x) \d x$ is an integral over $x$.
\end{document}

ただし、コマンドを通常のマクロのように定義し、\mathdef{\d}{\mathrm{d}}引数も取れるようにしたいのですが、、などを使ったすべての実験はexpandafterうまくunexpandedいかず、いつもの奇妙なエラー メッセージしか表示されません。どうすればいいのか、何かヒントはありますか?

\everymathさらに、大きなドキュメントでそのように使用すると、パフォーマンスに大きなペナルティが発生すると思いますか?

答え1

私のアイデアは egreg のアイデアと非常に似ていますが、オプションの引数を追加して、math コマンドが引数自体を処理できるようにしたいと考えています。コード:

\documentclass{article}

\usepackage{xparse}
\DeclareDocumentCommand{\mathdef}{mO{0}m}{%
  \expandafter\let\csname old\string#1\endcsname=#1
  \expandafter\newcommand\csname new\string#1\endcsname[#2]{#3}
  \DeclareRobustCommand#1{%
    \ifmmode
      \expandafter\let\expandafter\next\csname new\string#1\endcsname
    \else
      \expandafter\let\expandafter\next\csname old\string#1\endcsname
    \fi
    \next
  }%
}

\mathdef{\v}[1]{\tilde{#1}}
\mathdef{\d}{\mathrm{d}}

\begin{document}
Ha\v{c}ek and tilde $\v{a}+\v{b}=1$.

\d x  is an x with a dot below and $\int f(x) \d x$ is an integral over $x$.
\end{document}

結果:

ここに画像の説明を入力してください

答え2

私は使用しません\everymath

\documentclass{article}
\usepackage{letltxmacro}

\makeatletter
\newcommand{\mathdef}[2]{%
  \@ifundefined{#1}{\@mathdef@new{#1}{#2}}{\@mathdef@remember{#1}{#2}}
}

\newcommand{\@mathdef@remember}[2]{%
  \expandafter\LetLtxMacro
    \csname textmode@#1\expandafter\endcsname
    \csname #1\endcsname
  \expandafter\def\csname mathmode@#1\endcsname{#2}%
  \expandafter\DeclareRobustCommand\csname#1\endcsname{%
    \ifmmode\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
     {\csname mathmode@#1\endcsname}{\csname textmode@#1\endcsname}%
  }%
}
\newcommand{\@mathdef@new}[2]{%
  \expandafter\DeclareRobustCommand\csname#1\endcsname{#2}%
}
\makeatother

\mathdef{d}{\mathop{}\!\mathrm{d}}

\begin{document}

\d{x} is an x with a dot below and $\int f(x) \d x$ is an integral over $x$.

\end{document}

ここに画像の説明を入力してください

しかし、これは良い方法ではないと思います。混乱を招き、エラーが発生しやすくなります。

見る\LetLtxMacro はいつ使用すればよいですか?の詳細については、 をご覧ください\LetLtxMacro

次のようにすると少し簡単になりますetoolbox:

\documentclass{article}
\usepackage{etoolbox}

\makeatletter
\newcommand{\mathdef}[2]{%
  \@ifundefined{#1}{\@mathdef@new{#1}{#2}}{\@mathdef@remember{#1}{#2}}
}

\newcommand{\@mathdef@remember}[2]{%
  \expandafter\robustify\csname#1\endcsname
  \csletcs{textmode@#1}{#1}%
  \csdef{mathmode@#1}{#2}%
  \protected\csdef{#1}{%
    \ifmmode\expandafter\@firstoftwo\else\expandafter\@secondoftwo\fi
     {\csuse{mathmode@#1}}{\csuse{textmode@#1}}%
  }%
}
\newcommand{\@mathdef@new}[2]{%
  \protected\csdef{#1}{#2}%
}
\makeatother

\mathdef{d}{\mathop{}\!\mathrm{d}}

\begin{document}

\d{x} is an x with a dot below and $\int f(x) \d x$ is an integral over $x$.

\end{document}

\DeclareRobustCommandなぜ(最初のバージョン) または\protected(2 番目のバージョン) を全面的に使用するのでしょうか?

TeXが書き込み操作を行うときは、モードにはなりません。特に数式モードではありません。次のような単純な定義は、

\newcommand{\foo}{%
  \relax % if this comes first in an alignment
  \ifmmode
    \expandafter\@firstoftwo
  \else
    \expandafter\@secondoftwo
  \fi
  \foo@math\foo@text
}

だろういつも\foo@textで見つかった場合は選択します\write。上記の保護により、\mathdefコマンドはそれ自体で書き込まれるため、その時点では選択は行われません\write

なぜ を使用しないのですか\everymath? 次の例を試して確認してください。

\documentclass{article}

\everymath{\let\foo\foomath}
\newcommand{\foo}{Foo}
\newcommand{\foomath}{Ops}

\begin{document}

Text: \foo

Math: $\foo$

Ops: abc\textsuperscript{\foo}

\end{document}

答え3

使用したい\PushPostHook-マクロあらゆるフック引数を処理するマクロを(再)定義するためのパッケージですか?

問題 1:

#1LaTeX では、引数は やなどで表されます#2。つまり、1..9 の範囲の数字が続くハッシュのシーケンスで表されます。

定義をネストする場合は、内部定義のハッシュを 2 倍にする必要があります。

\def\outsidemacro#1{%
  \def\insidemacro##1{This is insidemacro's argument: ##1.}%  
  This is outsidemacro's argument: #1.%
}

拡張中、連続する 2 つのハッシュが 1 つのハッシュに統合され、ハッシュの量が半分になります。

つまり、次\outsidemacro{foo}のようになります。

\def\insidemacro#1{This is insidemacro's argument: #1.}%  
This is outsidemacro's argument: foo.%

\PushPostHookviaの定義を見ると\show\PushPostHook次のようになります。

> \PushPostHook=\protected\long macro:
#1#2->\eh@checkhook {#1}\PushPostHook \letcs \eh@tempi {eh@post#1}\expandafter 
\gdef \csname eh@post#1\expandafter \endcsname \expandafter {\eh@tempi \eh@hook
separator #2}\undef \eh@tempi .

ご覧のとおり、トークンはパターン の名前を持つマクロ内に保持されます 。\eh@post⟨name of hook⟩

例えば、\eh@postmathおよび \eh@postdisplay

このようなマクロにトークンを追加するには、\PushPostHook問題のマクロを次のようにします。\eh@tempi、問題のトークンをマクロの後ろに追加してマクロを再定義します。拡大\eh@tempi

拡大は\eh@tempi重要なポイントです:

\eh@tempiの定義にハッシュ ( ) が含まれている場合#、展開中に 2 つの連続するハッシュがそのうちの 1 つに縮小されます。

これは次のことを意味します:

または\PushPostHookにものを追加するために が呼び出されるたびに、または にすでに含まれているものを含む連続ハッシュの数が半分になります。\eh@postmath\eh@postdisplay\eh@postmath\eh@postdisplay

これは、 または\PushPostHookに何かを追加するために複数回呼び出す場合に特に問題になります。\eh@postmath\eh@postdisplay

トークン レジスタを使用して を維持することで、連続するハッシュの量が半分になることを回避できます\the。これは、トークン レジスタの内容が によって「吐き出される」ときに、ハッシュの量が減らないためです。 の間に による「吐き出し」が\the発生すると\edef、ハッシュの量は減らないだけでなく、2 倍になります。

例えば、

\myscratchtoks{#}
\edef\mymacro{\the\myscratchtoks}

の定義では、\mymacroから得られるハッシュの量\myscratchtoksは 2 倍になります。 を展開すると\mymacro、その 2 倍になった量は半分になり、したがって の展開は\mymacroによって提供されるのと同じ量のハッシュを提供します\the\myscratchtoks

したがって、トークン レジスタに何かを追加し、 \PushPostHookそのトークン レジスタを「フラッシュ」するためだけに使用することをお勧めします。

問題2:

\long引数も処理できるマクロを維持したい場合は、プレフィックスまたはと同様に使用できるものを実装することをお勧めします\global

以下の例では、括弧で囲まれた引数の後に括弧でネストされた引数が続く#{マクロを実装するために -記法を使用しました。マクロの は常に括弧でネストされるため、 の前にあるすべてのトークンを取得するために、左括弧で区切られた引数の処理を使用できます。 このようなトークンには、定義コマンド自体 (またはなど)、(再)定義される制御シーケンス トークン、および があります。\mathcommand⟨definition text⟩⟨definition text⟩
\renewcommand*\global\long\def⟨parameter text⟩

例えば、

\mathcommand\renewcommand*\mymacrowitharguments[2]{%
  \mbox{math-arg~1: }(#1) 
  \mbox{ math-arg~2: }(#2)
}%

の最初の(\mathcommand左中括弧で区切られた)引数はシーケンスになり\renewcommand*\mymacrowitharguments[2]、2 番目の引数は中括弧内にネストされた内容によって形成されます。 は、\mbox{math-arg~1: }(#1) \mbox{ math-arg~2: }(#2)シーケンス
\mathcommand、つまり、シーケンスをトークン レジスタに追加します。⟨first argument⟩{⟨second argument⟩}\renewcommand*\mymacrowitharguments[2]{\mbox{math-arg~1: }(#1) \mbox{ math-arg~2: }(#2) }\mymathhooktoks

また、左中括弧で区切られた引数を取得し、その引数の後ろにある、つまり次のように形成できる制御シーケンス トークンの名前として中括弧内にネストされているものを取得するマクロも実装\mathcommandfromnameしました\csname..\endcsname

例えば、

\mathcommandfromname\renewcommand*{mymacrowitharguments}[2]{%
  \mbox{math-arg~1: }(#1) 
  \mbox{ math-arg~2: }(#2)
}%

結果:

\mathcommand\renewcommand*\mymacrowitharguments[2]{%
  \mbox{math-arg~1: }(#1) 
  \mbox{ math-arg~2: }(#2)
}%

\documentclass{article}

\usepackage{everyhook}

\newtoks\myscratchtoks
\newcommand\mymathhookmacro{}%

\PushPostHook{math}{\mymathhookmacro}%
\PushPostHook{display}{\mymathhookmacro}%

\newcommand{\mathcommand}{}%
\long\def\mathcommand#1#{\innermathcommand{#1}}%
\newcommand{\innermathcommand}[2]{%
  \begingroup
  \expandafter\myscratchtoks\expandafter{\mymathhookmacro#1{#2}}%
  \xdef\mymathhookmacro{\the\myscratchtoks}%
  \endgroup
}

\newcommand\exchange[2]{#2#1}%
\newcommand\mathcommandfromname{}%
\long\def\mathcommandfromname#1#{\romannumeral0\innermathcommandfromname{#1}}%
\newcommand\innermathcommandfromname[2]{%
  \expandafter\exchange\expandafter{\csname#2\endcsname}{ \mathcommand#1}%
}%

%----------------------------------------------------------------

\newcommand*\mymacrowitharguments[2]{%
  non-math-arg~1: \textbf{(#1)} %
  non-math-arg~2: \textbf{(#2)}%
}%

\mathcommand\renewcommand*\mymacrowitharguments[2]{%
  \mbox{math-arg~1: }(#1)
  \mbox{ math-arg~2: }(#2)
}%

\newcommand*\myothermacrowitharguments[2]{%
  other-non-math-arg~1: \textbf{(#1)} %
  other-non-math-arg~2: \textbf{(#2)}%
}%

\mathcommandfromname\renewcommand*{myothermacrowitharguments}[2]{%
  \mbox{other-math-arg~1: }(#1)
  \mbox{ other-math-arg~2: }(#2)
}%

\mathcommand\renewcommand*\d{\mathrm{d}}%

\parindent=0ex

\begin{document}

\d x  is an x with a dot below and $\int f(x) \d x$ is an 
integral over $x$.

\bigskip

Testing with \verb|\mymacrowitharguments|:\smallskip

outside math:\\
\mymacrowitharguments{arg A}{arg B}
\smallskip

inside math:\\
$\mymacrowitharguments{arg A}{arg B}$
\bigskip

Testing with \verb|\myothermacrowitharguments|:\smallskip

outside math:\\
\myothermacrowitharguments{arg A}{arg B}
\smallskip

inside math:\\
$\myothermacrowitharguments{arg A}{arg B}$

\end{document}

ここに画像の説明を入力してください

関連情報