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 have some LaTeX3 code and I want to add a new feature. This will involve choosing between various actions at a particular point. There are two choices to be made, and these choices are independent. The times when I read in the choice and the time when the choice is acted upon are distinct, so I need to store the choice for a bit before reading it back. As there is one choice for each term in a list, I need to store the choices in a similar list. I can think of a variety of ways of doing this and am wondering if there is a current proper way (couldn't find it in texdoc interface3) or, if not, a particular way that is better than the others.

Here are some ideas:

  1. This is how I would do it in a language like lua: store an integer or string in the range 0-3 and test the bits. This is, though, a bit messy with integers (if i%2 == 0 then is simple enough but the second bit is more like if (i/2)%2 < 1 then) and easier with strings if s:sub(1,1) == 0 then).
  2. Define two new booleans and store the requisite "setting" macros. So in a token list, I store the tokens \bool_set_true:N \l_bool_a \bool_set_false:N \l_bool_b and then execute it before doing \bool_if:NTF to figure out the actions to take.

Here's what I might do in a LaTeX2 scenario in a one-shot version:

\documentclass{article}

\newif\ifchoicea
\newif\ifchoiceb

\def\saveifs{%
  \edef\restoreifs{\expandafter\noexpand\csname choicea\ifchoicea
true\else false\fi\endcsname\expandafter\noexpand\csname
choiceb\ifchoiceb true\else false\fi\endcsname}
}

\def\stateofifs{%
  Choice A is \ifchoicea true\else false\fi,
  choice B is \ifchoiceb true\else false\fi.
}
\begin{document}

\stateofifs

\choiceatrue
\saveifs

\stateofifs

\emph{You wait.
Time passes}

\choicebtrue
\choiceafalse

\stateofifs

\restoreifs

\stateofifs
\end{document}

This produces:

Choice A is false, choice B is false.
Choice A is true, choice B is false.
You wait. Time passes
Choice A is false, choice B is true.
Choice A is true, choice B is false.

In my real use case, I'll store the "states" in an array. So in pseudo-code it might be something like:

while (getting_data) do
    process_data
    convert_choice_to_storable_form
    push(choice_array,storeable_form)
end

do
    something_complicated
end

while (rendering_data) do
    pop(choice_array,storeable_form)
    convert_storable_form_to_choices
    foreach choice do
        if choice then
            do_something_stupid
        else
            do_something_clever
        fi
    end
end
share|improve this question
    
Questions: In the real use case, what's the setting interface? Is the key here performance or clarity? Do you need a save/restore stack, or is it a simple one-level save? –  Joseph Wright Sep 28 '12 at 7:02
    
One level save. The top-level interface is via keys (it's for the hobby package): the user will use a pgf key of the form blank=true (or some alias to that). I now have it that that constructs an l3key (also blank=true, the second layer is needed to allow for aliases). All of these keys are read before the processing starts so they need to be stored in an array. After the heavy work is done, they need to be read back to determine the choices to be made. There's no nesting involved: the processing is a closed box and can't be interrupted. –  Andrew Stacey Sep 28 '12 at 8:50
    
Why can't you just save the key list in a token list and \keys_set:nV when needed? For aliases, that's what .meta:n is intended for. –  Joseph Wright Sep 28 '12 at 8:56
    
Maybe I can. But I need to read and act on some keys before the machine starts and some keys afterwards. So I read them all at the start and store those that I don't need until later. Storing them as a key list is one way, but as I've already read them it seems inefficient. All the information can be encoded in a 2-bit number and at this point there is no need for higher abstraction. –  Andrew Stacey Sep 28 '12 at 9:12
add comment

1 Answer

I would maintain two property lists; one is updated when a condition is set, the second one is loaded with the state of the first when \saveifs is issued. Then the "static" one can be used to restore the conditionals to the fixed state.

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn
% Document commands
\NewDocumentCommand{\setchoice}{ m m }
 {
  (Setting~#1~to~#2)\par
  \andrew_setchoice:nn { #1 } { #2 }
 }
\NewDocumentCommand{\saveifs}{ }
 {
  (Saving ifs)\par
  \andrew_saveifs:
 }
\NewDocumentCommand{\restoreifs}{ }
 {
  (Restoring ifs)\par
  \andrew_restoreifs:
 }
\NewDocumentCommand{\stateofifs}{} % no internal version, it's just for showing the situation
 {
  State of ifs:\par
  \prop_if_empty:NT \l__andrew_conditionals_prop { *Still~no~conditionals* }
  \prop_map_inline:Nn \l__andrew_conditionals_prop 
   {
    Choice~##1~is~##2\par
   }
 }

% Variables
\prop_new:N \l__andrew_conditionals_prop
\prop_new:N \l__andrew_saved_conditionals_prop

% Functions

\cs_new_protected:Npn \andrew_setconditional:nn #1 #2
 {
  \use:c { bool_set_#2:c } { l__andrew_cond_ #1 _bool }
  \prop_put:Nnn \l__andrew_conditionals_prop { #1 } { #2 }
 }
\cs_new_protected:Npn \andrew_setchoice:nn #1 #2
 {
  \cs_if_free:cT { l__andrew_cond_ #1 _bool }
   {
    \bool_new:c { l__andrew_cond_ #1 _bool }
   }
  \andrew_setconditional:nn { #1 } { #2 }
 }
\cs_new_protected:Npn \andrew_saveifs:
 {
  \prop_set_eq:NN \l__andrew_saved_conditionals_prop \l__andrew_conditionals_prop
 }
\cs_new_protected:Npn \andrew_restoreifs:
 {
  \prop_map_inline:Nn \l__andrew_saved_conditionals_prop
   {
    \andrew_setconditional:nn { ##1 } { ##2 }
   }
 }
\ExplSyntaxOff

\begin{document}

\stateofifs

\setchoice{a}{true}
\saveifs

\stateofifs

\setchoice{b}{true}
\setchoice{a}{false}

\stateofifs
\saveifs

\setchoice{a}{true}

\restoreifs

\stateofifs
\end{document}

The conditionals are dynamically created. So \setchoice{foo}{true} will create \l__andrew_cond_foo_bool if needed, but it's not necessary to know that name. It will also update the property list \l__andrew_conditionals_prop.

The command \saveifs will set \l__andrew_saved_conditionals_prop equal to the current \l__andrew_conditionals_prop. For restoring the values it's sufficient to do a mapping from this property list.

enter image description here

share|improve this answer
    
I'm trying to put my finger on what I don't like about this solution, and it's probably my fault for complicating the issue. In the above, it feels as though the booleans aren't really used. My use-case is all internal to L3 so I can just check the value stored in the prop, and instead of true and false I could store 1 and 0, then as I know the number of conditionals I could simply store them as a token list as, say, 10 for "a is true, b is false". –  Andrew Stacey Sep 26 '12 at 18:29
    
@AndrewStacey It all depends on what you really want to do. If the conditionals are all known, it's a different thing and a numerical comparison might be better. But maintaining the conditionals may be more efficient if the decision has to be made depending on one of them. If they are only two, then the branches are four; if they are ten, then an \int_case:nnn with 1024 branches is not that maintainable. –  egreg Sep 26 '12 at 19:20
    
That last bit is what I meant about the conditionals being independent. So I don't have to consider 1024 cases. I have to consider 10 lots of 2 cases. Choice A influences action A but has no effect on action B which is determined by Choice B. –  Andrew Stacey Sep 26 '12 at 21:46
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.