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'm not getting the output I want from my LaTeX code, and I think the problem is that I'm not expanding things at the right times.

Code

\documentclass{article}

\usepackage{tikz}
\usepackage{etextools}

\makeatletter

% The result of calling
%   \@defineLine{foo}{35}
% is that the command \wickerson@foo@pos gets defined as 35.
\newcommand*\@defineLine[2]{%
  \typeout{Defining wickerson@#1@pos = #2}
  \expandafter\xdef\csname wickerson@##1@pos\endcsname{#2}
}

% Project the pos-field from the line with the given
% identifier. For instance, if \wickerson@foo@pos=35 then
%   \@getLinePos{foo}
% will give 35.
\xdef\@getLinePos#1{\csname wickerson@#1@pos\endcsname}

% A comma-separated list of "active" lines. Each element is
% a line identifier. 
\newcommand*\@activeLines{}

% Add the given identifier to the list of active lines.
\newcommand*\@addToActiveLines[1]{%
  \ifx\@activeLines\@empty\else\g@addto@macro\@activeLines{,}\fi
  \g@addto@macro\@activeLines{#1}
}

% Remove the given identifier from the list of active lines.             
\newcommand*\@removeFromActiveLines[1]{%                                 
  \@expandtwoargs\@removeelement{#1}\@activeLines\@activeLines           
}                                                                        

% Print each active line identifier, associated with its
% corresponding pos field. Typical output:
%   {a ↦ 10, b ↦ 30, c ↦ 50, d ↦ 70,}
\newcommand*\@printState{%
  Current state: $\{
  \foreach\i in \@activeLines {%
    \i \mapsto \@getLinePos\i,
  }
  \}$ \par
}

\begin{document}

\foreach \x/\xvalue in {a/10, b/30, c/50, d/70} {
  \ExpandNextTwo\@defineLine{\x}{\xvalue}
  \ExpandNext\@addToActiveLines{\x}
  \@printState
} 
\foreach \x in {a, c} {                                                  
  \ExpandNext\@removeFromActiveLines{\x}                                 
  \@printState                                                           
}                                                                        

\end{document}

Explanation

The first foreach-loop cycles over a list of pairs, building up a function from identifiers (e.g. a,b,etc.) to numbers. The values of this function are stored in a series of macros (e.g. \wickerson@a@pos stores the value of the function for identifier a), and the domain of this function is the comma-separated list \@activeLines. Initially \@activeLines is empty, and by the end it is a,b,c,d.

I print my function after each iteration of the loop. By the end I expect to print

{a↦10, b↦30, c↦50, d↦70}

but instead it prints

{a↦70, b↦70, c↦70, d↦70}

I suspect the problem is when I add a mapping to my function, I'm not expanding the value enough, and so it changes when I add another mapping. I thought using ExpandNextTwo from etextools would expand everything nicely, but it doesn't seem to have helped. Any ideas?

The second foreach-loop tries to remove some of the mappings from my function, with the help of the \@removeelement command. Unfortunately, it doesn't work. The mapping is only removed within the scope of the current loop iteration, and by the next iteration, the mapping has returned. I suspect the problem is that the \@removeelement is not redefining \@activeLines globally, but I don't know how to fix this. Any ideas? (I hope I don't need to switch to the expl3 syntax to achieve this, because that would be require quite a drastic change to my code!)

Current output

enter image description here

Desired output

enter image description here

share|improve this question
add comment

2 Answers

up vote 18 down vote accepted

The following two macros need fixing:

\newcommand*\@defineLine[2]{%
  \typeout{Defining wickerson@#1@pos = #2}
  \expandafter\xdef\csname wickerson@##1@pos\endcsname{#2}
}

That will define for \@defineLine{a}{10} the macro \wickerson@#1@pos instead of \wickerson@a@pos. Fix:

\newcommand*\@defineLine[2]{%
  \typeout{Defining wickerson@#1@pos = #2}
  \expandafter\xdef\csname wickerson@#1@pos\endcsname{#2}
}

The next macro uses it:

% Project the pos-field from the line with the given
% identifier. For instance, if \wickerson@foo@pos=35 then
%   \@getLinePos{foo}
% will give 35.
\xdef\@getLinePos#1{\csname wickerson@#1@pos\endcsname}

Since \xdef expands, \csname is executed to \wickerson@#1@pos with #1 part of the macro name, not an argument. Fix:

\gdef\@getLinePos#1{\csname wickerson@#1@pos\endcsname}

Getting rid of the last comma

A feature of \foreach can be used to get rid of the latest comma in the output:

\newcommand*\@printState{%
  Current state: $\{
  \foreach\i [count=\ii] in \@activeLines {%
    \ifnum\ii>1 ,\fi
    \i \mapsto \@getLinePos\i
  }
  \}$ \par
}

Result

Removing

The question uses the following definition for removing elements:

\newcommand*\@removeFromActiveLines[1]{%                                 %
  \@expandtwoargs\@removeelement{#1}\@activeLines\@activeLines           %
}                                                                        %

Indeed, the effect of LaTeX's \@removeelement is limited to the current group. If you want to make the changes of \@activeLines global, then \global\let helps:

\newcommand*\@removeFromActiveLines[1]{%                                 %
  \@expandtwoargs\@removeelement{#1}\@activeLines\@activeLines           %
  \global\let\@activeLines=\@activeLines
}                                                                        %
share|improve this answer
    
Thank you so much Heiko. Your explanations are extremely clear to me. –  John Wickerson May 16 '13 at 19:37
    
So I wasn't actually defining \wickerson@a@pos, \wickerson@b@pos, etc, but instead defining \wickerson@#1@pos over and over again. Heh. –  John Wickerson May 16 '13 at 19:53
    
Transparent PNG background... I like it. –  Werner May 16 '13 at 20:19
2  
@Werner Thanks. The transparent background is the result of ImageMagick's convert from PDF to PNG. It uses Ghostscript with device pngalpha. –  Heiko Oberdiek May 16 '13 at 20:26
1  
@JohnWickerson Answer updated (using \global\let\@activeLines=\@activeLines). –  Heiko Oberdiek May 17 '13 at 12:46
show 2 more comments

I believe that with expl3 it's more straightforward, since it doesn't rely on knowing what's needed to expand and when.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
\seq_new:N \l_wickerson_list_seq

\NewDocumentCommand{\printState}{>{\SplitList{,}}m}
 {
  \seq_clear:N \l_wickerson_list_seq
  \ProcessList{#1}{\splitatslash}
  Current~state\tl_to_str:n { : } ~ %
  $\seq_use:Nnnn \l_wickerson_list_seq { , } { , } { , }$
 }
\NewDocumentCommand{\splitatslash}{>{\SplitArgument{1}{/}}m}
 {
  \wickerson_split_element:nn #1
 }
\cs_new_protected:Npn \wickerson_split_element:nn #1 #2
 {
  \seq_put_right:Nn \l_wickerson_list_seq { #1 \mapsto #2 }
 }
\ExplSyntaxOff

\begin{document}
\printState{a/10}

\printState{a/10, b/30}

\printState{a/10, b/30, c/50}

\printState{a/10, b/30, c/50, d/70}
\end{document}

enter image description here


A different implementation that makes it possible to remove states; I've used both a sequence for the "before slash" parts and a property list that holds the vaule ("after slash").

The command \defineStates reinitializes the variables, while \addStates put new states in the variables; similarly, \removeStates removes states. Finally, \printStates just gives a visual representation of the states.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

%%% variables
\seq_new:N \g_wickerson_list_seq
\prop_new:N \g_wickerson_states_prop

%%% document commands
\NewDocumentCommand{\defineStates}{m}
 {
  \wickerson_definestates:n { #1 }
 }
\NewDocumentCommand{\addStates}{m}
 {
  \wickerson_addstates:n { #1 }
 }
\NewDocumentCommand{\removeStates}{m}
 {
  \clist_map_inline:nn { #1 }
   {
    \seq_gremove_all:Nn \g_wickerson_list_seq { ##1 }
    % The following also removes the state from the property list
    % You can choose whether keep it or comment it out
    \prop_gremove:Nn \g_wickerson_states_prop { ##1 }
   }
 }
\NewDocumentCommand{\printStates}{}
 {
  Current~state\tl_to_str:n { : } ~ %
  $
   \seq_map_inline:Nn \g_wickerson_list_seq
    { ##1 \mapsto \prop_get:Nn \g_wickerson_states_prop { ##1 }, }
  $
 }

%%% internal commands
\cs_new_protected:Npn \wickerson_addstates:n #1
 {
  \clist_map_inline:nn { #1 }
   {
    \wickerson_split_element:ww ##1 \q_stop
   }
 }
\cs_new_protected:Npn \wickerson_definestates:n #1
 {
  \seq_gclear:N \g_wickerson_list_seq
  \prop_gclear:N \g_wickerson_states_prop
  \wickerson_addstates:n { #1 }
 }
\cs_new_protected:Npn \wickerson_split_element:ww #1 / #2 \q_stop
 {
  \seq_gput_right:Nn \g_wickerson_list_seq { #1 }
  \prop_gput:Nnn \g_wickerson_states_prop { #1 } { #2}
 }

\ExplSyntaxOff

\begin{document}
%% Initialize
\defineStates{a/10,b/30}\printStates

%% Add new states
\addStates{c/50}\printStates

\addStates{d/70,e/90}\printStates

%% remove some states
\removeStates{a,c}\printStates

\end{document}

enter image description here

share|improve this answer
    
I think it would be more natural to use a delimited function at the expl3 level, not xparse. Namely, \cs_new_protected:Npn \wickerson_split_element:ww #1 / #2 \q_stop {...}. –  Bruno Le Floch May 17 '13 at 12:48
1  
@BrunoLeFloch I agree, but I wanted to use less expl3 as possible. I'll provide a "full" `expl3 version later. –  egreg May 17 '13 at 12:53
    
I'm trying to learn more about this expl3 stuff, but so far have only found a short introduction (here‌​). Where can I learn about things like what {>{\SplitList{,}}m} means? –  John Wickerson May 18 '13 at 15:46
1  
@JohnWickerson That's in the documentation of xparse: texdoc xparse –  egreg May 18 '13 at 15:47
add comment

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.