Take the 2-minute tour ×
TeX - LaTeX Stack Exchange is a question and answer site for users of TeX, LaTeX, ConTeXt, and related typesetting systems. It's 100% free, no registration required.

I want to dynamically define some macros that essentially create a list of \refs. I seem to be able to define these macros but then I am unable to print them.

Here is my M(non)WE:

\documentclass{article}
\makeatletter
\def\Assign#1{% if \DP@#1 is defined append to it otherwise create it
  \ifcsname DP@#1\endcsname\def\DP@tmp{\csname DP@#1\endcsname,\@currentlabel}
  \else\def\DP@tmp{\@currentlabel}
  \fi
  \expandafter\edef\csname DP@#1\endcsname{\DP@tmp}
  \typeout{ASSIGN: \csname DP@#1\endcsname}
}
\def\Assigned#1{#1: \csname DP@#1\endcsname}
                \typeout{ASSIGNED: \csname DP@#1\endcsname}
}
\makeatother

\begin{document}

\begin{enumerate}
  \item Apples \Assign{fred}
  \item Oranges \Assign{julie}
  \item Bananas \Assign{fred}
  \item Mangos \Assign{julie}
  \item Strawberries \Assign{julie}
\end{enumerate}

\Assigned{fred}   % should print 1,3
\Assigned{julie}  % should print 2,4,5

\end{document}

I want the \Assigned{} macro to print out the item numbers "assigned" to that person. That is, after the itemise environment above I should just get:

fred: 1,3
julie: 2,4,5

Unfortunately, I am missing the item numbers and I just get:

fred:
julie:

The M(non)WE latex's without any problems but, for reasons that I don't understand, the \Assigned macros are not expanding. I have tried many variations using \expandafter 1 or more times, \edef, ... but nothing seems to work.

In the log file the \typeout{}'s calls above are producing:

ASSIGN: 1
ASSIGN: 2
ASSIGN: 1,3
ASSIGN: 2,4
ASSIGN: 2,4,5
ASSIGNED: `\DP@fred`
ASSIGNED: `\DP@julie`

So it looks as if the \DP@#1's are bing created correctly and it is the \Assigned{} macros that is not working, which is probably an expansion problem. Using \edef doesn't work...but the fact that the expansion issue occurs INSIDE the \typeout suggests to me that I am doing something more fundamentally wrong.

Can anyone tell me how to fix this?

Next on my wish-list would be to have it print "1,3-6,8" rather than "1,3,4,5,6,8", for example. But first things first!

share|improve this question
    
Welcome to TeX.SX! A tip: You can use backticks ` to mark your inline code as I did in my edit. Also you don't have to sign with your name since it automatically appears in the lower right corner of your post. –  Adam Liter Mar 14 at 2:58
    
Oh, so you didn't want them below the itemize environment? You wanted them in the log? My answer is completely wrong-headed then and I will delete it shortly. (I assume you wanted it in the log based on the answer you accepted but that's not at all how I understood your question. The itemize environment is not, after all, in the log...) –  cfr Mar 14 at 3:08
    
@cfr I do want the numbers in the text - the \typeout's were for debugging. I gave the tick to Heiko because he explained what the problem was and how to fix it. –  Andrew Mar 14 at 3:22
    
Oh, OK. Thanks for responding. I wasn't complaining about who you'd given the tick to - it just made me think I'd answered something which wasn't your question and should delete it. But I guess I won't since even though mine is essentially the same as others', it was mine and I don't think I've ever come up with an answer to a csname/expand type question 'all by myself' before! –  cfr Mar 14 at 3:34

4 Answers 4

up vote 9 down vote accepted

The environment enumerate adds a group, thus the changes to \DP@#1 are local and lost after the environment closes. Thus the example makes the definition global:

\documentclass{article}

\makeatletter
\def\Assign#1{% if \DP@#1 is defined append to it otherwise create it
  \ifcsname DP@#1\endcsname
    \edef\DP@tmp{\csname DP@#1\endcsname,\@currentlabel}%
  \else
    \edef\DP@tmp{\@currentlabel}%
  \fi
  \global\expandafter\let\csname DP@#1\endcsname\DP@tmp
  \typeout{ASSIGN: \csname DP@#1\endcsname}
}
\def\Assigned#1{%
  % \expandafter\def\csname Assigned#1\endcsname{\csname DP@#1\endcsname}
  \typeout{ASSIGNED: \csname DP@#1\endcsname}
}
\makeatother

\begin{document}

\begin{enumerate}
  \item Apples \Assign{fred}  
  \item Oranges \Assign{julie}
  \item Bananas \Assign{fred}
  \item Mangos \Assign{julie}
  \item Strawberries \Assign{julie}
\end{enumerate}

\Assigned{fred}   % should print 1,3  
\Assigned{julie}  % should print 2,4,5

\end{document}

Result:

ASSIGN: 1
ASSIGN: 2
ASSIGN: 1,3
ASSIGN: 2,4
ASSIGN: 2,4,5
ASSIGNED: 1,3
ASSIGNED: 2,4,5

Update for range compression

The example parses the comma list of \DP@#1 and defines \Assign#1 with compressed ranges for consecutive entries:

\documentclass{article}

\usepackage{kvoptions}

\makeatletter
\def\Assign#1{% if \DP@#1 is defined append to it otherwise create it
  \ifcsname DP@#1\endcsname
    \edef\DPx@tmp{\csname DP@#1\endcsname,\@currentlabel}%
  \else
    \edef\DPx@tmp{\@currentlabel}%
  \fi
  \global\expandafter\let\csname DP@#1\endcsname\DPx@tmp
  \typeout{ASSIGN: \csname DP@#1\endcsname}
}
\def\Assigned#1{%
  \begingroup
    \let\DPx@curr\@empty
    \let\DPx@tmp\@empty
    \expandafter\expandafter\expandafter\comma@parse
    \expandafter\expandafter\expandafter{\csname DP@#1\endcsname}%
    \AssignedAux
    \ifx\DPx@curr\@empty
    \else
      \edef\DPx@tmp{\DPx@tmp-\DPx@curr}%
    \fi
    \global\expandafter\let\csname Assigned#1\endcsname\DPx@tmp
  \endgroup
  \typeout{ASSIGNED: \csname Assigned#1\endcsname}
}
\def\AssignedAux#1{%
  \ifx\DPx@tmp\@empty
    \def\DPx@tmp{#1}%
    \count@=#1\relax
  \else
    \advance\count@\@ne
    \ifnum#1=\count@
      \def\DPx@curr{#1}%
    \else
      \count@=#1\relax
      \ifx\DPx@curr\@empty
        \edef\DPx@tmp{\DPx@tmp,#1}%
      \else
        \edef\DPx@tmp{\DPx@tmp-\DPx@curr,#1}%
        \let\DPx@curr\@empty
      \fi
    \fi
  \fi
}
\makeatother

\begin{document}

\begin{enumerate}
  \item Apples \Assign{fred}
  \item Oranges \Assign{julie}
  \item Bananas \Assign{fred}
  \item Mangos \Assign{julie}\Assign{fred}
  \item Strawberries \Assign{julie}
  \item Pears \Assign{julie}
  \item Lemons \Assign{fred}
\end{enumerate}

\Assigned{fred}   % should print 1,3
\Assigned{julie}  % should print 2,4,5

\end{document}

Result:

ASSIGN: 1
ASSIGN: 2
ASSIGN: 1,3
ASSIGN: 2,4
ASSIGN: 1,3,4
ASSIGN: 2,4,5
ASSIGN: 2,4,5,6
ASSIGN: 1,3,4,7
ASSIGNED: 1,3-4,7
ASSIGNED: 2,4-6
share|improve this answer
    
Thank you! I should have thought of this:) –  Andrew Mar 14 at 3:09
    
Thank yu Heiko! In light of your edit I should clarify my previous remark as saying that I should have noticed the global/local problem...your solution to my compression question is beyond the call of duty and much appreciated. Thanks! –  Andrew Mar 14 at 3:46
    
Very nice update for compression. –  Steven B. Segletes Mar 14 at 10:37

The syntax \def\Assigned#1{} is incorrect. The proper way is \expandafter\def\csname Assigned#1\endcsname{}. That allows your code to compile. Also, the \edef is localized and can't be accessed at a higher level. Changing it to \xdef remedies that.

\documentclass{article}
\makeatletter
\def\Assign#1{% if \DP@#1 is defined append to it otherwise create it
  \ifcsname DP@#1\endcsname\def\DP@tmp{\csname DP@#1\endcsname,\@currentlabel}
  \else\def\DP@tmp{\@currentlabel}
  \fi
  \expandafter\xdef\csname DP@#1\endcsname{\DP@tmp}
  \typeout{ASSIGN: \csname DP@#1\endcsname}
}
\def\Assigned#1{\expandafter\def\csname Assigned#1\endcsname{\csname DP@#1\endcsname}
                \typeout{ASSIGNED: \csname DP@#1\endcsname}
}
\makeatother

\begin{document}

\begin{enumerate}
  \item Apples \Assign{fred}
  \item Oranges \Assign{julie}
  \item Bananas \Assign{fred}
  \item Mangos \Assign{julie}
  \item Strawberries \Assign{julie}
\end{enumerate}

\Assigned{fred}   % should print 1,3
\Assigned{julie}  % should print 2,4,5

\end{document}

The log file contains:

ASSIGN: 1
ASSIGN: 2
ASSIGN: 1,3
ASSIGN: 2,4
ASSIGN: 2,4,5
ASSIGNED: 1,3
ASSIGNED: 2,4,5
share|improve this answer
    
Thanks for the reply. Unfortunately this is not what I am looking for. The \Assigned{} macro is meant to print the item numbers assigned to that person rather than create a new macro. I have edited my question to try and make this clearer. (Oops, I see that I posted the wrong code into my question, which probably didn't help. Sorry!) –  Andrew Mar 14 at 2:51
    
@Andrew see revision –  Steven B. Segletes Mar 14 at 2:55
    
Thanks Steven. This works. I'm ticking this but giving the green tick to Heiko because his answer explains that my problem is a local/global issue. Thanks again! –  Andrew Mar 14 at 3:04
    
@Andrew Glad you got it resolved. –  Steven B. Segletes Mar 14 at 3:07
\documentclass{article}
\makeatletter
\def\Assign#1{% if \DP@#1 is defined append to it otherwise create it
  \ifcsname DP@#1\endcsname\def\DP@tmp{\csname DP@#1\endcsname,\@currentlabel}
  \else\def\DP@tmp{\@currentlabel}
  \fi
  \expandafter\xdef\csname DP@#1\endcsname{\DP@tmp}
  \typeout{ASSIGN: \csname DP@#1\endcsname}
}
\def\Assigned#1{%
  \csname DP@#1\endcsname
                \typeout{ASSIGNED: \csname DP@#1\endcsname}
}
\makeatother

\begin{document}

\begin{enumerate}
  \item Apples \Assign{fred}
  \item Oranges \Assign{julie}
  \item Bananas \Assign{fred}
  \item Mangos \Assign{julie}
  \item Strawberries \Assign{julie}
\end{enumerate}

\Assigned{fred}   % should print 1,3
\Assigned{julie}  % should print 2,4,5

\end{document}

Assignments

Response to updated question

To get the names as well as the item numbers:

\documentclass{article}
\makeatletter
\def\Assign#1{% if \DP@#1 is defined append to it otherwise create it
  \ifcsname DP@#1\endcsname\def\DP@tmp{\csname DP@#1\endcsname,\@currentlabel}
  \else\def\DP@tmp{\@currentlabel}
  \fi
  \expandafter\xdef\csname DP@#1\endcsname{\DP@tmp}
  \typeout{ASSIGN: \csname DP@#1\endcsname}
}
\def\Assigned#1{%
  #1: \csname DP@#1\endcsname
                \typeout{ASSIGNED: \csname DP@#1\endcsname}
}
\makeatother

\begin{document}

\begin{enumerate}
  \item Apples \Assign{fred}
  \item Oranges \Assign{julie}
  \item Bananas \Assign{fred}
  \item Mangos \Assign{julie}
  \item Strawberries \Assign{julie}
\end{enumerate}

\Assigned{fred}   % should print 1,3
\Assigned{julie}  % should print 2,4,5

\end{document}

Assignments by name

share|improve this answer
    
Note that I posted this partly because I'd managed to puzzle it out - even though I then saw 2 people had posted solutions. Having seen those solutions, though, I'm sort of curious whether I'd just misunderstood the question or whether I'm just missing something else. (Not for the definition of \Assign which seems to be pretty much the same - but for \Assigned.) I'm especially curious because the other solutions post the log whereas I've posted the resulting document. Again, maybe I've just confused what's wanted? –  cfr Mar 14 at 3:02
    
Thanks! The two solutions essentially do the right thing...the confusion was caused by my not cutting my code in the question proprely. –  Andrew Mar 14 at 3:14
    
@Andrew Yes. The other solutions got edited after I wrote that comment, I think. (As did mine.) –  cfr Mar 14 at 3:36

You may appreciate a LaTeX3 version;

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\NewDocumentCommand{\Assign}{m}
 {
  \andrew_assign:n { #1 }
 }
\NewDocumentCommand{\Assigned}{m}
 {
  \andrew_assigned:n { #2 }
 }

% syntactic sugar for shortening code
\cs_new:Npn \__andrew_seq:n #1
 {
  g_andrew_assignee_#1_seq
 }

\cs_new_protected:Npn \andrew_assign:n #1
 {% if the assignee is not yet defined, create a sequence
  \seq_if_exist:cF { \__andrew_seq:n { #1 } }
   {
    \seq_new:c { \__andrew_seq:n { #1 } }
   }
  % globally add the item to the assignee's sequence
  \seq_gput_right:cv { \__andrew_seq:n { #1 } } { @currentlabel }
 }

\cs_new:Npn \andrew_assigned:n #1
 {% print the assignee's items separated by a comma
  \seq_use:cn { \__andrew_seq:n { #1 } } { ,~ }
 }

\cs_generate_variant:Nn \seq_gput_right:Nn { cv }
\cs_generate_variant:Nn \seq_use:Nn { c }

\ExplSyntaxOff

\begin{document}
\begin{enumerate}
  \item Apples \Assign{fred}
  \item Oranges \Assign{julie}
  \item Bananas \Assign{fred}
  \item Mangos \Assign{julie}
  \item Strawberries \Assign{julie}
\end{enumerate}

Fred has \Assigned{fred}   % should print 1,3

Julie has \Assigned{julie}  % should print 2,4,5

\end{document}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.