动态宏定义问题

动态宏定义问题

问题的答案很可能就埋藏在该站点的某个地方——我对宏扩展的理解有限,这似乎阻碍了我的搜索……

我正在尝试定义可以像这样使用的宏:

\begin{document}

  \AimTaskDef{1}{task 1}
  \AimTaskAdd{1}{task 2}

  \AimTaskDef{2}{task 3}
  \AimTaskAdd{2}{task 4}

  ...
  % later, in some table
  \AimTaskGet{1}

  \AimTaskGet{2}

这将导致如下输出

\begin{itemize}
 \item task 1
 \item task 2
\end{itemize}

\begin{itemize}
 \item task 3
 \item task 4
\end{itemize}

我已设置以下宏:

 \def\AimTaskDef#1#2{%
   \expandafter\def\csname AimTask#1\endcsname{ \item #2}
 }

 \def\AimTaskAdd#1#2{%
   \expandafter\def\tmp{\expandafter\csname AimTask#1\endcsname}
   \expandafter\def\csname AimTask#1\endcsname{ \tmp \item #2}
 }

 \def\AimTaskGet#1{ \begin{itemize} \csname AimTask#1\endcsname \end{itemize} }

我对“tmp”的定义/使用存在错误,因为我看到以下错误:

\AimTaskAdd #1#2->\expandafter \def \tmp
                                         {\expandafter \csname AimTask#1\end...
l.28       \AimTaskAdd{1}{task 2}

?
! Missing control sequence inserted.
<inserted text>
                \inaccessible
l.28       \AimTaskAdd{1}{task 2}

?

结果仅包含最后指定的任务。如果您能对 \tmp 发生了什么有任何见解,我将不胜感激。

非常感谢,安德烈。

答案1

下面的操作可以实现你想要的效果(但需要 e-TeX;也可以修改为不使用 e-TeX 也可以工作):

在此处输入图片描述

\documentclass{article}

\makeatletter
\def\AimTaskDef#1#2{%
  \expandafter\def\csname AimTask#1\endcsname{ \item #2}%
}
\def\AimTaskAdd#1#2{%
  \ifcsname AimTask#1\endcsname
    % Check for repeated entries (AM):
    \@expandtwoargs\in@
      {\detokenize{\item #2}}
      {\detokenize\expandafter\expandafter\expandafter{\csname AimTask#1\endcsname}}%
    \ifin@\else
      \expandafter\g@addto@macro\csname AimTask#1\endcsname{\item #2}%
    \fi      
  \else
    \AimTaskDef{#1}{#2}%
  \fi%
}
\def\AimTaskGet#1{\begin{itemize}\csname AimTask#1\endcsname\end{itemize}}
\makeatother
\begin{document}

Explicit definition:

\begin{itemize}
 \item task 1
 \item task 2
\end{itemize}

\begin{itemize}
 \item task 3
 \item task 4
\end{itemize}

\ldots

Macro definition:

\AimTaskAdd{1}{task 1}
\AimTaskAdd{1}{task 2}
\AimTaskDef{2}{task 3}
\AimTaskAdd{2}{task 4}
\AimTaskAdd{2}{task 4} % Repeated (AM)


\AimTaskGet{1}

\AimTaskGet{2}
\end{document}

为了将内容附加到现有宏,使用 就足够了\g@addto@macro{<macro>}{<stuff>}。此外,我已更新\AimTaskAdd为检查 是否存在AimTask#1,如果不存在则创建它。这样您就可以这样做

\AimTaskAdd{1}{task 1}
\AimTaskAdd{1}{task 2}

而不必担心AimTask1已经存在(我也在我的例子中使用了这一点作为说明)。

答案2

具有设施的实施expl3

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\AimTaskDef}{ m m }{ \andre_task_def:nn { #1 } { #2 } }
\NewDocumentCommand{\AimTaskAdd}{ m m }{ \andre_task_add:nn { #1 } { #2 } }
\NewDocumentCommand{\AimTaskGet}{ m }
 {
  \begin{itemize} \prop_get:Nn \g_andre_tasks_prop { #1 } \end{itemize}
 }

\prop_new:N \g_andre_tasks_prop
\msg_new:nnn { andre } { task-exist }
 {
  Task~#1~already~exists:~I'm~overwriting~it
 }
\msg_new:nnn { andre } { task-not-exist }
 {
  Task~#1~doesn't~exist~yet:~I'm~defining~it
 }

\cs_new_protected:Npn \andre_task_def:nn #1 #2
 {
  \prop_if_in:NnT \g_andre_tasks_prop { #1 } 
   {
    \msg_warning:nnx { andre } { task-exist } { #1 }
   }
  \prop_gput:Nnn \g_andre_tasks_prop { #1 } { \item #2 }
 }
\cs_new_protected:Npn \andre_task_add:nn #1 #2
 {
  \prop_if_in:NnTF \g_andre_tasks_prop { #1 }
   {
    \prop_gput:Nnx \g_andre_tasks_prop { #1 }
     {
      \prop_get:Nn \g_andre_tasks_prop { #1 } \exp_not:n { \item #2 }
     }
   }
   {
    \msg_warning:nnx { andre } { task-not-exist } { #1 }
    \prop_gput:Nnn \g_andre_tasks_prop { #1 } { \item #2 }
   }
 }
\ExplSyntaxOff


\begin{document}

\AimTaskDef{1}{task 1}
\AimTaskAdd{1}{task 2}

\AimTaskDef{2}{task 3}
\AimTaskAdd{2}{task 4}

\AimTaskGet{1}

\AimTaskGet{2}

\AimTaskDef{1}{task 1}
\AimTaskAdd{3}{task !}
\end{document}

如果宏\AimTaskDef用于已定义的任务,则会发出警告,在这种情况下,会覆盖它(这可以更改);如果宏\AimTaskAdd用于尚未定义的任务,它们也会发出警告(并定义它)。

一个可能更有效的版本(感谢 Bruno Le Floch):

\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\AimTaskDef}{ m m }{ \andre_task_def:nn { #1 } { #2 } }
\NewDocumentCommand{\AimTaskAdd}{ m m }{ \andre_task_add:nn { #1 } { #2 } }
\NewDocumentCommand{\AimTaskGet}{ m }
 {
  \begin{itemize} \prop_get:Nn \g_andre_tasks_prop { #1 } \end{itemize}
 }

% Variables    
\prop_new:N \g_andre_tasks_prop
\tl_new:N \l_andre_temp_tl

% Messages
\msg_new:nnn { andre } { task-exists }
 {
  Task~#1~already~exists:~I'm~overwriting~it
 }
\msg_new:nnn { andre } { task-exists-not }
 {
  Task~#1~doesn't~exist~yet:~I'm~defining~it
 }

% Functions
\cs_new_protected:Npn \andre_task_def:nn #1 #2
 {
  \prop_if_in:NnT \g_andre_tasks_prop { #1 } 
   {
    \msg_warning:nnx { andre } { task-exists } { #1 }
   }
  \prop_gput:Nnn \g_andre_tasks_prop { #1 } { \item #2 }
 }
\cs_new_protected:Npn \andre_task_add:nn #1 #2
 {
  \prop_get:NnNTF \g_andre_tasks_prop { #1 } \l_andre_temp_tl
   {
    \tl_put_right:Nn \l_andre_temp_tl { \item #2 }
    \prop_gput:NnV \g_andre_tasks_prop { #1 } \l_tmpa_tl
   }
   {
    \msg_warning:nnx { andre } { task-exists-not } { #1 }
    \prop_gput:Nnn \g_andre_tasks_prop { #1 } { \item #2 }
   }
 }
\ExplSyntaxOff


\begin{document}

\AimTaskDef{1}{task 1}
\AimTaskAdd{1}{task 2}

\AimTaskDef{2}{task 3}
\AimTaskAdd{2}{task 4}

\AimTaskGet{1}

\AimTaskGet{2}

\AimTaskDef{1}{task 1}
\AimTaskAdd{3}{task !}
\end{document}

相关内容