1

I'm attempting to create documents of a priori unknown length that is determined by a set of conditions. I'm generating LaTeX variables, including conditional variables, using Python. Within a variables LaTeX file, I have something resembling:

\newcommand{\VariableList}{VariableOne,VariableTwo}
\newcommand{\VariableOneSubOne}{SomeValueOne}
\newcommand{\VariableOneSubTwo}{SomeOtherValueOne}
\newif\ifVariableOneCondOne
\VariableOneCondOnetrue
\newcommand{\VariableTwoSubOne}{SomeValueTwo}
\newcommand{\VariableTwoSubTwo}{SomeOtherValueTwo}
\newif\ifVariableTwoCondOne
\VariableTwoCondOnefalse

Where the number of variables are not known. I loop over each variable and have some text that includes the values dynamically defined by the variables.

\foreach \n in \VariableList{
Here is some text with \inputnum{\n}{SubOne} and \inputnum{\n}{SubTwo}.
}

Where I have defined:

\newcommand{\inputnum}[2]{\expandafter\csname #1#2\endcsname}

How can I define a similar command to take advantage of the conditional variables I have defined above? For example, I tried:

\newcommand{\inputcondnum}[2]{\expandafter\csname if#1#2\endcsname}

using the logic that the macro would replace

\inputcondnum{\n}{CondOne} 

with the following

\ifVariableOneCondOne

but that does not seem to work. Any suggestions on how to use these dynamically generated conditional variables?

Here is a minimum example:

\documentclass{article}
\usepackage{pgffor}

% Command to generate variables
\newcommand{\inputnum}[2]{\expandafter\csname #1#2\endcsname}
\newcommand{\inputcondnum}[2]{\expandafter\csname if#1#2\endcsname}

% Variable definitions
\newcommand{\VariableList}{VariableOne,VariableTwo}
\newcommand{\VariableOneSubOne}{SomeValueOne}
\newcommand{\VariableOneSubTwo}{SomeOtherValueOne}
\newif\ifVariableOneCondOne
\VariableOneCondOnetrue
\newcommand{\VariableTwoSubOne}{SomeValueTwo}
\newcommand{\VariableTwoSubTwo}{SomeOtherValueTwo}
\newif\ifVariableTwoCondOne
\VariableTwoCondOnefalse


\begin{document}

\foreach \n in \VariableList{
Here is some text with \inputnum{\n}{SubOne} and \inputnum{\n}{SubTwo}. 

\inputcondnum{\n}{CondOne} Include this text if true \else Include this text if false. \fi
}
\end{document} 
  • See above for the test file. – DrTRD Nov 22 '17 at 18:45
4

It's generally best to avoid the issue but you can make it work as

\documentclass{article}
\usepackage{pgffor}

% Command to generate variables
\newcommand{\inputnum}[2]{\expandafter\csname #1#2\endcsname}
\newcommand{\inputcondnum}[2]{\expandafter\csname if#1#2\endcsname}

% Variable definitions
\newcommand{\VariableList}{VariableOne,VariableTwo}
\newcommand{\VariableOneSubOne}{SomeValueOne}
\newcommand{\VariableOneSubTwo}{SomeOtherValueOne}
\newif\ifVariableOneCondOne
\VariableOneCondOnetrue
\newcommand{\VariableTwoSubOne}{SomeValueTwo}
\newcommand{\VariableTwoSubTwo}{SomeOtherValueTwo}
\newif\ifVariableTwoCondOne
\VariableTwoCondOnefalse


\begin{document}

\foreach \n in \VariableList{
Here is some text with \inputnum{\n}{SubOne} and \inputnum{\n}{SubTwo}. 

\iftrue\csname fi\endcsname\inputcondnum{\n}{CondOne} Include this text if true \else Include this text if false. \fi
}
\end{document}

the issue is that TeX skips over \if....\fi constructs but it needs to see \if... at the same time as it does not do expansion while skipping over "false" sections.

Note also that the \expandafter are not needed in

\newcommand{\inputnum}[2]{\expandafter\csname #1#2\endcsname}
\newcommand{\inputcondnum}[2]{\expandafter\csname if#1#2\endcsname}

in the first case, because \csname expands its content anyway so forcing expansion earlier with \expandafter results in the same name being constructed. In the second case the \expandafter does nothing at all as i is not expandable.

| improve this answer | |
  • This solution absolutely works, but if you could define inputcondnum, how would you do it to preserve the same variable definitions in the preamble and minimize the code when calling the variable? In other words, how would you define the command \inputcondnum so that it works as \iftrue\csname fi\endcsname\inputcondnum ? – DrTRD Nov 22 '17 at 18:57
  • Up to you, just only changing the line \newcommand{\inputcondnum}[2]{\expandafter\csname if#1#2\endcsname} – DrTRD Nov 22 '17 at 19:08
  • @jfbu 17 minutes ago I was eating dinner, I added some notes about that, then noticed your comment just now:-) – David Carlisle Nov 22 '17 at 19:23
  • 3
    @DrTRD if you want to use the \if..\else\fi syntax I would use \if\inputnum{\n} yes\else no\fi where inputnum was defined to be 00 for cases that should be true and 01 for cases that you want to be false. (then you don't need any \newif` – David Carlisle Nov 22 '17 at 19:25
  • This suggestion really is parsimonious. Thank you! – DrTRD Nov 22 '17 at 19:31
1

Resurrecting this old question to leave a somewhat cleaner solution, just for posterity.

If, for whatever reason, you're committed to the \csname if...\endcsname construct (say, if you need to pass the names of already-defined conditionals), a much cleaner solution than the \iftrue\csname fi\endcsname\inputcondnum... hackery is to simply put the entire \if...\fi construct inside a macro.

The problem arises when your \csname if...\endcsname is nested inside another conditional—in this case, a hidden one controlling the \foreach loop. To elaborate a little on David's answer, when executing a conditional, TeX does some bookkeeping to track of nested \if...\fis. If a conditional is false, it skips ahead to the next \fi (or next \else), but it needs to know which \fi to skip to. When the skipped text contains a second \if...\fi, it takes note, and makes sure to continue past the inner \fi and keep skipping ahead to the outer \fi. However, it doesn't perform any expansions while it's looking for its \fi. As a result, when the inner \if... is hidden inside another macro (like \csname...\endcsname) but the \fi is still in plain view, its bookkeeping gets thrown off, and everything goes haywire.

The solution is to make sure the \if... and the matching \fi are both tucked away inside a macro, making sure one is never seen without the other. In your case, you'd modify the minimal example to something like this (with a little formatting for readability):

\documentclass{article}
\setlength{\parindent}{0pt}\setlength{\parskip}{10pt} %readability
\usepackage{pgffor}

% Command to generate variables
\newcommand{\inputnum}[2]{\csname #1#2\endcsname}
\newcommand{\inputcondnum}[2]{\csname if#1#2\endcsname}

% Variable definitions
\newcommand{\VariableList}{VariableOne,VariableTwo}

\newcommand{\VariableOneSubOne}{SomeValueOne}
\newcommand{\VariableOneSubTwo}{SomeOtherValueOne}
\newcommand{\VariableTwoSubOne}{SomeValueTwo}
\newcommand{\VariableTwoSubTwo}{SomeOtherValueTwo}

\newif\ifVariableOneCondOne
\newif\ifVariableTwoCondOne
\VariableOneCondOnetrue
\VariableTwoCondOnefalse

%COMMAND TO TEST CONDITIONAL AND EXECUTE DESIRED CODE
\newcommand{\testcondnum}[4]{\inputcondnum{#1}{#2} #3 \else #4 \fi}

\begin{document}

%A simple implementation

{\bfseries Simple}:

\foreach \n in \VariableList{%
    \texttt{\textbackslash n} is \n.\\%
    Here is some text with \inputnum{\n}{SubOne} and \inputnum{\n}{SubTwo}. \\%
    \testcondnum{\n}{CondOne}{Include this text if true.}{Include this text if false.}

}

%If you want vary what's going on inside the conditional,
%re-define a macro on each pass through the loop with the
% relevant code to execute. You'll probably want to define 
%it with \def here, since \def doesn't care about overwriting 
%old commands. With \newcommand and \renewcommand, you'd need
%to worry about using one on the first pass and the other 
%on subsequent passes.

%Of course, you could also just keep feeding the "Include 
%this..." bits straight into testcondnum, but for more 
%complicated code, using macros will probably be easier.

{\bfseries Dynamic}:

\foreach \n in \VariableList{    
    \def\dothisiftrue{Include this text if \n CondOne is true.}
    \def\dothisiffalse{Include this text if \n CondOne is false.}  
    \texttt{\textbackslash n} is \n.\\%
    Here is some text with \inputnum{\n}{SubOne} and \inputnum{\n}{SubTwo}. \\%
    \testcondnum{\n}{CondOne}{\dothisiftrue}{\dothisiffalse}

}

\end{document}

Compiling yields:

enter image description here

| improve this answer | |

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.