我正在尝试创建由一组条件决定的先验未知长度的文档。我正在使用 Python 生成 LaTeX 变量,包括条件变量。在变量 LaTeX 文件中,我有类似以下内容:
\newcommand{\VariableList}{VariableOne,VariableTwo}
\newcommand{\VariableOneSubOne}{SomeValueOne}
\newcommand{\VariableOneSubTwo}{SomeOtherValueOne}
\newif\ifVariableOneCondOne
\VariableOneCondOnetrue
\newcommand{\VariableTwoSubOne}{SomeValueTwo}
\newcommand{\VariableTwoSubTwo}{SomeOtherValueTwo}
\newif\ifVariableTwoCondOne
\VariableTwoCondOnefalse
变量数量未知。我循环遍历每个变量,并有一些包含由变量动态定义的值的文本。
\foreach \n in \VariableList{
Here is some text with \inputnum{\n}{SubOne} and \inputnum{\n}{SubTwo}.
}
我定义的地方:
\newcommand{\inputnum}[2]{\expandafter\csname #1#2\endcsname}
我如何定义类似的命令来利用上面定义的条件变量?例如,我尝试过:
\newcommand{\inputcondnum}[2]{\expandafter\csname if#1#2\endcsname}
使用宏将替换的逻辑
\inputcondnum{\n}{CondOne}
以下内容
\ifVariableOneCondOne
但这似乎不起作用。关于如何使用这些动态生成的条件变量有什么建议吗?
这是一个最小的例子:
\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}
答案1
通常最好避免这个问题,但你可以让它工作
\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}
问题是 TeX 跳过了\if....
\fi 构造,但它需要\if...
同时查看,因为它在跳过“错误”部分时不会进行扩展。
还请注意\expandafter
,
\newcommand{\inputnum}[2]{\expandafter\csname #1#2\endcsname}
\newcommand{\inputcondnum}[2]{\expandafter\csname if#1#2\endcsname}
在第一种情况下,因为\csname
无论如何都会扩展其内容,所以强制提前扩展\expandafter
会导致构造相同的名称。在第二种情况下,\expandafter
由于不可i
扩展,因此什么也不做。
答案2
重新提出这个老问题是为了留下一个更清晰的解决方案,仅供后人参考。
如果出于某种原因,您致力于构造\csname if...\endcsname
(例如,如果您需要传递已定义的条件的名称),那么比黑客更干净的解决方案\iftrue\csname fi\endcsname\inputcondnum...
就是将整个\if...\fi
构造简单地放在宏中。
\csname if...\endcsname
当你的条件语句嵌套在另一个条件语句中时,就会出现问题——在本例中,是一个控制\foreach
循环的隐藏条件语句。为了详细阐述 David 的回答,在执行条件语句时,TeX 会进行一些记录来跟踪嵌套的条件\if...\fi
语句。如果条件语句为假,它会跳到下一个\fi
(或下一个\else
),但它需要知道哪个 \fi
跳转到。当跳过的文本包含第二个时\if...\fi
,它会记录下来,并确保继续越过内部\fi
并继续跳转到外部\fi
。然而,它在寻找 时不会执行任何扩展\fi
。因此,当 内部\if...
隐藏在另一个宏(如\csname...\endcsname
)中,但\fi
仍然在视图中时,它的簿记就会被打乱,一切都会变得混乱。
解决方案是确保\if...
和匹配\fi
是两个都隐藏在宏中,确保其中一个永远不会在没有另一个的情况下出现。对于您的情况,您可以将最小示例修改为如下内容(略作格式化以提高可读性):
\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}
编译结果: