这是我的项目的简化版本。我有一个名为 的宏\CreateMacro
,它需要 2 个输入,即要创建的宏名称及其主体。例如,调用\CreateMacro{Newton}{$F=ma$.}
将创建一个\Newton
带有主体的新宏$F=ma$
。在幕后,有一个辅助宏\Newtonprivate
被创建。 的目的\Newtonprivate
是用不同的样式格式化主体,在此示例中,将主体设为红色。
每次\Newton
调用时,\Newtonprivate
都必须将其推送到类似数组的列表中。\Render
然后打印与推送\Newtonprivate
次数相同的次数。\Newtonprivate
平均能量损失
\documentclass{book}
\usepackage{xcolor}
\makeatletter
\newcommand\CreateMacro[2]{%
\expandafter\@ifdefinable\csname#1private\endcsname{\global\@namedef{#1private}{\textcolor{red}{#2}}}% auxiliary private macro
\expandafter\@ifdefinable\csname#1\endcsname{\global\@namedef{#1}{#2\expandafter\addjword{\csname #1private\endcsname}}}%
}
\usepackage{etoolbox}
% Array for words
\newcounter{jwordcount}%
\newcommand\setjword[2]{\csdef{jword#1}{#2}}%
\newcommand\addjword[1]{\stepcounter{jwordcount}\setjword{\thejwordcount}{#1}}%
\newcommand\getjword[1]{\csuse{jword#1}}%
%%% Basic Loop
%Taken from the aloop pof David Salomon's The Advanced TeXbook pg 191
%Use:
%\newcount\temp
%\loop\temp=1 step 1 until 10 do {something} \endloop\temp
\def\loop#1=#2 {%
\long\def\next step ##1 until ##2 do ##3 \endloop#1{%
##3%
\advance#1 by ##1
\ifnum#1>##2\relax\else\next step ##1 until ##2 do ##3 \endloop#1 \fi%
}%end of \next
#1=#2 \next}%end of \loop
%Return the table containing the current words, reset the counter
\newcommand\render{%
\newcount\temp\loop\temp=1 step 1 until {\thejwordcount} do {\expandafter\getjword{\@arabic\temp}\ifnum\temp<\thejwordcount\else\fi} \endloop\temp%
\setcounter{jwordcount}{0}%reset for the next group of words
}
\makeatother
\parindent=0pt
\begin{document}
\CreateMacro{Newton}{$F=ma$.}
\chapter{\protect\Newton}
\render
\Newton\\
\render
\Newton\\
\render
\end{document}
问题
当我调用Newton
inside时\chapter
,列表似乎仍然是空的。不应该这样。如何解决这个问题?
答案1
章节标题按组排版,并且列表不会全局更新。使用\csgdef
。
一些其他怪癖。
\expandafter\addjword{\csname #1private\endcsname}
尝试扩展{
,完全无用。要么删除\expandafter
,要么执行\expandafter\addjword\expandafter{\csname #1private\endcsname}
同样
\expandafter\getjword{\@arabic\temp}
地,应该\expandafter\getjword\expandafter{\the\temp}
(
\the
比 更有效\@arabic
)。\newcount\temp
应该超出定义范围\render
,否则每次调用都会浪费一个计数器。你正在进行的循环非常麻烦。
以下是 的一个版本expl3
:
\documentclass{book}
\usepackage{xparse}
\usepackage{xcolor}
\ExplSyntaxOn
\NewDocumentCommand{\CreateMacro}{mm}
{
\cs_new_protected:cpn { #1 }
{
#2
\seq_gput_right:Nn \g_eirian_push_seq { \eirian_format:n { #2 } }
}
}
\NewDocumentCommand{\render}{ }
{
\seq_use:Nn \g_eirian_push_seq {}
\seq_gclear:N \g_eirian_push_seq
}
\seq_new:N \g_eirian_push_seq
\cs_new_protected:Nn \eirian_format:n
{
\textcolor{red}{#1}
}
\ExplSyntaxOff
\parindent=0pt
\begin{document}
\CreateMacro{Newton}{$F=ma$.}
\chapter{\Newton}
\render
\Newton\Newton\\
\render
\Newton\\
\render
\end{document}
由于\Newton
是“受保护的”,所以您不需要\protect
在它前面。
“传统”版本,灵活性较差。
\documentclass{book}
\usepackage{xcolor}
\usepackage{etoolbox}
\newcommand{\CreateMacro}[2]{%
\expandafter\newrobustcmd\csname #1\endcsname{%
#2\gappto\pushed{\format{#2}}%
}%
}
\newcommand{\format}[1]{\textcolor{red}{#1}}
\newcommand{\render}{\pushed\gdef\pushed{}}
\parindent=0pt
\begin{document}
\CreateMacro{Newton}{$F=ma$.}
\chapter{\Newton}
\render
\Newton\Newton\\
\render
\Newton\\
\render
\end{document}
您可以推送多个内容并独立呈现它们的版本(当然,\render
需要有一个参数)。
\documentclass{book}
\usepackage{xparse}
\usepackage{xcolor}
\ExplSyntaxOn
\NewDocumentCommand{\CreateMacro}{mm}
{
\seq_new:c { g_eirian_push_#1_seq }
\cs_new_protected:cpn { #1 }
{
#2
\seq_gput_right:cn { g_eirian_push_#1_seq } { \eirian_format:n { #2 } }
}
}
\NewDocumentCommand{\render}{m}
{
\seq_use:cn { g_eirian_push_#1_seq } {~}
\seq_gclear:c { g_eirian_push_#1_seq }
}
\cs_new_protected:Nn \eirian_format:n
{
\textcolor{red}{#1}
}
\ExplSyntaxOff
\parindent=0pt
\begin{document}
\CreateMacro{Newton}{$F=ma$.}
\CreateMacro{Einstein}{$E=mc^2$.}
\chapter{\Newton}
\render{Newton}
\Newton\Einstein\Newton\\
\render{Newton}\render{Einstein}
\end{document}
答案2
你的\csdef
in\setjword
未能在该组中存活,但我\csgdef
存活了下来。
但说实话,我觉得代码可以重新整理一下。我会尝试在以后改进它。
\documentclass{book}
\usepackage{xcolor}
\usepackage{etoolbox}
\makeatletter
\newcommand\CreateMacro[2]{%
\expandafter\@ifdefinable\csname#1private\endcsname{\global\@namedef{#1private}{\textcolor{red}{#2}}}% auxiliary private macro
\expandafter\@ifdefinable\csname#1\endcsname{\global\@namedef{#1}{#2\expandafter\addjword{\csname #1private\endcsname}}}%
}
\usepackage{etoolbox}
% Array for words
\newcounter{jwordcount}%
\newcommand\setjword[2]{\csgdef{jword#1}{#2}}%
\newcommand\addjword[1]{\stepcounter{jwordcount}\setjword{\thejwordcount}{#1}}%
\newcommand\getjword[1]{\csuse{jword#1}}%
%%% Basic Loop
%Taken from the aloop pof David Salomon's The Advanced TeXbook pg 191
%Use:
%\newcount\temp
%\loop\temp=1 step 1 until 10 do {something} \endloop\temp
\def\loop#1=#2 {%
\long\def\next step ##1 until ##2 do ##3 \endloop#1{%
##3%
\advance#1 by ##1
\ifnum#1>##2\relax\else\next step ##1 until ##2 do ##3 \endloop#1 \fi%
}%end of \next
#1=#2 \next}%end of \loop
%Return the table containing the current words, reset the counter
\newcommand\render{%
\newcount\temp\loop\temp=1 step 1 until {\thejwordcount} do {\expandafter\getjword{\@arabic\temp}\ifnum\temp<\thejwordcount\else\fi} \endloop\temp%
\setcounter{jwordcount}{0}%reset for the next group of words
}
\makeatother
\parindent=0pt
\begin{document}
\CreateMacro{Newton}{$F=ma$.}
\chapter{\protect\Newton}
Render here: \render
Again: \Newton\\
\render
\Newton\\
\render
\end{document}
答案3
似乎这里有些事情过于复杂了。在纯 TeX 中,可以通过以下方式解决您的任务:
\input opmac
\def\renderlist{}
\def\createmacro#1#2{\addprotect#1%
\def#1{#2\global\addto\renderlist{\formatmacro{#2}}}}
\def\render{\renderlist\gdef\renderlist{}}
\def\formatmacro#1{{\localcolor\Red#1}}
\createmacro\Newton{$F=ma$.}
\chap \Newton
\render
\Newton:
\render
\Newton, \Newton:
\render
\end
如果你的环境是 LaTeX,那么使用包xcolor
并尝试
\def\renderlist{}
\def\createmacro#1#2{%
\protected\def#1{#2%
\expandafter\gdef\expandafter\renderlist\expandafter
{\renderlist\formatmacro{#2}}}}
\def\render{\renderlist\gdef\renderlist{}}
\def\formatmacro#1{\textcolor{red}{#1}}
\createmacro\Newton{$F=ma$.}
...