1

This may be a lot to answer at once, but I've come across some code in an old thread (pasted below) and don't really understand how it works. I would greatly appreciate it someone could walkthorugh the code and explain it? (specifically how the # in \def\Assign#1 works, the \ifcsname and \global operate)

\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}
  • It does print it out...in the log file. (that is what \typeout does). If you eliminate the word \typeout from the \Assigned definition, you will see it typeset in your document. – Steven B. Segletes Sep 27 '19 at 15:58
  • \csname DP@#1\endcsname are control sequences named, in your example, \DP@fred and \DP@julie. (of course you would need \makeatletter to access them in that form, without the \csname) – Steven B. Segletes Sep 27 '19 at 16:02
  • #1 is simply the first argument of the control sequence so in \def\foo#1 mean that \foo must have one argument (e.g. \foo{bah}) and \def\foo#1{\emph{#1}} mean that \foo will type the argument with emphasis (e.g. \foo{bah}will produce "bah"). – Fran Sep 28 '19 at 6:45
2

\def\foo#1{...#1...} is the primitive TeX way for defining one-argument macros.

In a LaTeX context, the code should have

\newcommand*{\Assign}[1]{...}

\newcommand*{\Assigned}[1]{...}

so as to check whether the macros are already defined and not be surprised of bizarre results.

The purpose of the code is to make an array of comma separated lists of numbers, each item is associated to a name and contains the list of tasks assigned to the person.

The task number is obtained with \@currentlabel, which expands to the representation of the last counter stepped with \refstepcounter; inside enumerate, \@currentlabel will expand to the current item number (with a prefix that, for first level enumerate environments, is empty).

The list of items is built incrementally and stored in a macro called \DP@<name>; for user fred, the macro is \DP@fred. However, using \DP@#1 would not work in the code, because of how TeX reads input and splits it into tokens, so \csname DP@#1\endcsname has to be used to form the correct macro name.

The conditional \ifcsname...\endcsname returns true if the control sequence that would result from \csname...\endcsname has a definition, false otherwise. The code uses this in order to test if \DP@<name> exists or not: in the first case the next item number will be appended after a comma, otherwise just the number is appended.

For instance, the first \Assign{fred} will define \DP@fred to be 1; the second one will append ,3.

The code does this in an indirect way, using the scratch macro \DP@tmp (which is a bad choice, because if one of the user names is tmp chaos would ensue).

With \edef\DP@tmp{...} full expansion of the replacement text is performed before doing the macro definition, which does the expected work.

Next, \DP@<name> is assigned the same meaning as the scratch macro; this has to be done globally, because local macro definition would be forgotten at the end of the enumerate environment.

The primitive instruction for assigning a control sequence the same meaning as another token is \let; if prefixed with \global, the assignment is global. However in this case we need to use \csname to form the assignee, but

\global\let\csname DP@#1\endcsname\DP@tmp

would not work, because it would make \csname the same as D. The control sequence token has to be formed before \let enters action, so \let has to be preceded by \expandafter. By rule of TeX a \global prefix recursively expands the next token until finding something it can be legally prefixed to (\let, \def and some other primitive commands). Hence

\global\expandafter\let\csname DP@#1\endcsname\DP@tmp

Actually the intermediate step with the scratch macro is not really needed:

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

would do as well and avoid the possible issue with a user named tmp.

The commented line in the definition of \Assigned would make a “user level” macro \Assigned<name>. If you uncomment it, the call \Assigned{fred} will print the list of tasks in the log file and the console, after making \Assignedfred equivalent to \DP@fred.

A better way to accomplish the same would be

\global\expandafter\let\csname Assigned#1\expandafter\endcsname\csname DP@#1\endcsname

I added \global for the same reason as before.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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