10

It is likely that the answer to the problem is buried on this site somewhere -- it is my limited understanding of macro expansion which seems to defy my search foo...

I am trying to define macros which I can use like this:

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

Which should result in output like this

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

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

I have the following macro set in place:

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

I am doing something wrong with the 'tmp' definition/usage, as I see the following errors:

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

?

and the result contains only the last specified task. I would appreciate any insight in what is going on with \tmp.

Many thanks, Andre.

1
  • Pretty sure your problem is here \expandafter\def\tmp. The \expandafter skips \def and tries to expand \tmp which hasn't been defined. I get an undefined control sequence at that point when copy/pasting your code into a document.
    – Scott H.
    Commented Jul 13, 2012 at 1:35

2 Answers 2

7

The following does what you're after (but requires e-TeX; it can be modified to work without as well):

enter image description here

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

In order to append things to an existing macro, it's sufficient to use \g@addto@macro{<macro>}{<stuff>}. Also, I've updated \AimTaskAdd to check for the existence of AimTask#1 and create it if it doesn't exist. That allows you to do

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

without being concerned that AimTask1 exists already (I've also used this in my example as an illustration).

1
  • \g@addto@macro -- cool! Excellent, that is exactly what I was looking for, really :-) Commented Jul 13, 2012 at 8:21
5

An implementation with expl3 facilities.

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

The macros warn if \AimTaskDef is used with an already defined task and, in this case, overwrite it (this could be changed); they also warn if \AimTaskAdd is used on a not yet defined task (and define it).

A possibly more efficient version (thanks to 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}
1
  • I would personally use \prop_get:NnNTF in \andre_task_add:nn to retrieve the old value in a temporary variable and test if_in at the same time. It may still be marked as an experimental function in the version on CTAN, but it is now not experimental anymore (upload to CTAN in a few days). The only drawback of using \prop_get:NnNTF is that you will need an auxiliary token list variable. Commented Jul 13, 2012 at 15:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .