延迟表达式评估给出错误结果

延迟表达式评估给出错误结果

我制作了自己的列表。我想在列表之外引用它们。如果我使用列表层次结构中某个点的计数器进行计算,例如\alph{listcounter},我如何在计算时冻结该结果,以便稍后引用它时它具有原始计算值。LaTeX 会延迟计算,\alph{listcounter}直到我最终将其放到页面上,此时该listcounter值不合适。

我决定不使用现成的列表包,并且确实需要一个可以普遍使用的解决方案来解决这个问题。我所追求的是一种将结果冻结到变量中的方法,而不是产生结果的计算。

我可以在文件中像这样冻结字符串,我认为这有点过分,但它表明字符串可以被冻结。我使用的字符串没有格式,因为这也造成了各种麻烦。我意识到将此例程推广到多个引用本身将是一场噩梦,但以下代码故意简单明了地演示了这个问题。

保存表达式值的快照的通常方法是什么?

我是这里的新手,但对 LaTeX 还只是半新手。过去我从这个网站得到了很多帮助。非常感谢。

\documentclass[12pt]{article}

\usepackage{amsmath}

\newcommand{\makeref}{Q\arabic{numqpoints}(\alph{numapoints})}

\def\qmg{1.4em}\def\amg{1.4em}

\newcounter{listnest}\setcounter{listnest}{1}
\newcounter{reftype}

\newcommand{\setlistbody}[1]
{
\setlength\parsep{3pt}
\setlength\labelsep{3pt}
\setlength\labelwidth{1.6em}
\ifnum\value{listnest}=1
    \setlength\leftmargin{0pt}
\else
    \setlength\leftmargin{\ifcase#1\or\qmg\or\amg\fi}
\fi
\setcounter{reftype}{#1}
\stepcounter{listnest}
}

\newcounter{numqpoints}
\newenvironment{qpoints}
%
{\begin{list}
{{\bf Q}\,\arabic{numqpoints}}
{\usecounter{numqpoints}\setlistbody1}
}
{\end{list}
    \addtocounter{listnest}{-1}
    \setcounter{numqpoints}{0}
}

\newcounter{numapoints}
\newenvironment{apoints}
%
{\begin{list}
    {(\alph{numapoints})}
    {\usecounter{numapoints}\setlistbody2}
}
{\end{list}
    \addtocounter{listnest}{-1}
    \setcounter{numapoints}{0}
}

\newwrite\tempfile
\newcommand{\freezestring}[1]
{
\immediate\openout\tempfile=lists.tex
\immediate\write\tempfile{#1}
\immediate\closeout\tempfile
\global\def\savestring{\input{lists}}
}

\begin{document}
%
\begin{qpoints}
\item One
\item Two
    \begin{apoints}
    \item AAA
    \item \freezestring{\makeref} \global\def\varstring{\makeref}
    BBB Here is what I want: \savestring
    \end{apoints}
\end{qpoints}
%
\par Here is what I get with a variable: \varstring
%
\par Here is what I get with a file: \savestring
%
\end{document}

附录 1:好的,现在似乎已经基本解决了,我将把它移到下一个复杂程度。我需要灵活地将这些字符串组合在一起,因为我希望列表级别以各种组合使用。我知道如何跟踪哪个级别是哪个,所以我只关注下一个引用字符串问题。使用循环构造字符串会破坏所\xdef提供的解决方案。以下循环可以独立运行,但在 中崩溃\xdef

以下是我的循环代码。我现在的要求是不是太多了?

\newcounter{nexttype}
\newcommand{\printref}
{
\setcounter{nexttype}{1}%
\loop%
\ifcase\value{nexttype}%
\or Q\arabic{numqpoints}%
\or (\alph{numapoints})%
\fi%
\ifnum\value{listnest}>\value{nexttype}%
\stepcounter{nexttype}%
\repeat%
}

附录 2:好吧,我显然没有要求太多,因为我自己解决了这个问题。我突然意识到我可以将内容附加到原始动态变量中,这样就不必直接将任何令人讨厌的东西传递给它。

\newcommand{\makerefvar}[2]{\expandafter\xdef\csname Wiz#1\endcsname{#2}}
\newcommand{\getref}[1]{\csname Wiz#1\endcsname}

\newcounter{thistype}
\newcommand{\makeref}[1]
{
\makerefvar{#1}{}\getref{#1}
\setcounter{thistype}{1}%
\loop%
\ifcase\value{thistype}%
\or \makerefvar{#1}{{\getref{#1}Q\arabic{numqpoints}}}%
\or \makerefvar{#1}{\getref{#1}(\alph{numapoints})}%
\or \makerefvar{#1}{\getref{#1}\roman{numrpoints}}%
\fi%
\ifnum\value{listnest}>\value{thistype}%
\stepcounter{thistype}%
\repeat%
}

答案1

如果您需要的参考总是晚于设置的参考,那么您可以使用\xdef合适的控制序列。

\documentclass[12pt]{article}

\usepackage{amsmath}

\newcommand{\makeref}[1]{%
  \expandafter\xdef\csname geoff@#1\endcsname{%
    Q\arabic{numqpoints}(\alph{numapoints})%
  }%
}
\newcommand{\getref}[1]{%
  \csname geoff@#1\endcsname
}

\def\qmg{1.4em}\def\amg{1.4em}

\newcounter{listnest}\setcounter{listnest}{1}
\newcounter{reftype}

\newcommand{\setlistbody}[1]{%
  \setlength\parsep{3pt}%
  \setlength\labelsep{3pt}%
  \setlength\labelwidth{1.6em}
  \ifnum\value{listnest}=1
    \setlength\leftmargin{0pt}%
  \else
    \setlength\leftmargin{\ifcase#1\or\qmg\or\amg\fi}%
  \fi
  \setcounter{reftype}{#1}%
  \stepcounter{listnest}
}

\newcounter{numqpoints}
\newenvironment{qpoints}
  {\begin{list}
   {\textbf{Q}\,\arabic{numqpoints}}
   {\usecounter{numqpoints}\setlistbody1}}
  {\end{list}%
   \addtocounter{listnest}{-1}%
   \setcounter{numqpoints}{0}}

\newcounter{numapoints}
\newenvironment{apoints}
  {\begin{list}
   {(\alph{numapoints})}
   {\usecounter{numapoints}\setlistbody2}}
  {\end{list}%
   \addtocounter{listnest}{-1}%
   \setcounter{numapoints}{0}}

\begin{document}
%
\begin{qpoints}
\item One
\item Two
    \begin{apoints}
    \item AAA
    \item \makeref{BBB}
    BBB Here is what I want: \getref{BBB}
    \end{apoints}
\end{qpoints}

Here is the reference: \getref{BBB}
%
\end{document}

不同的解决方案使用\label-\ref机制。

\documentclass[12pt]{article}

\usepackage{amsmath}

\makeatletter
\newcommand{\makeref}[1]{%
  \edef\@currentlabel{Q\arabic{numqpoints}(\alph{numapoints})}%
  \label{geoff@#1}
}
\makeatother
\newcommand{\getref}[1]{\ref{geoff@#1}}

\def\qmg{1.4em}\def\amg{1.4em}

\newcounter{listnest}\setcounter{listnest}{1}
\newcounter{reftype}

\newcommand{\setlistbody}[1]{%
  \setlength\parsep{3pt}%
  \setlength\labelsep{3pt}%
  \setlength\labelwidth{1.6em}
  \ifnum\value{listnest}=1
    \setlength\leftmargin{0pt}%
  \else
    \setlength\leftmargin{\ifcase#1\or\qmg\or\amg\fi}%
  \fi
  \setcounter{reftype}{#1}%
  \stepcounter{listnest}
}

\newcounter{numqpoints}
\newenvironment{qpoints}
  {\begin{list}
   {\textbf{Q}\,\arabic{numqpoints}}
   {\usecounter{numqpoints}\setlistbody1}}
  {\end{list}%
   \addtocounter{listnest}{-1}%
   \setcounter{numqpoints}{0}}

\newcounter{numapoints}
\newenvironment{apoints}
  {\begin{list}
   {(\alph{numapoints})}
   {\usecounter{numapoints}\setlistbody2}}
  {\end{list}%
   \addtocounter{listnest}{-1}%
   \setcounter{numapoints}{0}}

\begin{document}
%
\begin{qpoints}
\item One
\item Two
    \begin{apoints}
    \item AAA
    \item \makeref{BBB}
    BBB Here is what I want: \getref{BBB}
    \end{apoints}
\end{qpoints}

Here is the reference: \getref{BBB}
%
\end{document}

这是两种情况下的输出。第二种策略似乎是最好的,尽管它需要两次 LaTeX 运行才能稳定下来。

在此处输入图片描述

答案2

你在这里有一个很常见的误解:\global\def\varstring{\makeref}定义\varstring为扩展为\makeref价值\makeref。因此,当您使用时,\varstring您正在插入\makeref,然后扩展为

Q\arabic{numqpoints}(\alph{numapoints})

这样您就可以获得计数器的当前值。

相反,您需要使用扩展定义,因此\varstring包含 的当前外观\makeref。我们可以为此使用 TeX\xdef基元,但通常您不应该将 TeX 基元扩展与 LaTeX 材料一起使用。相反,我会使用\protected@xdef

\begin{qpoints}
\item One
\item Two
    \begin{apoints}
    \item AAA
    \item \freezestring{\makeref}
     \makeatletter\protected@xdef\varstring{\makeref}\makeatother
    \end{apoints}
\end{qpoints}

这是可行的,因为 LaTeX 会不断扩展,\makeref直到只剩下不可扩展的东西(数字、字母……)。

我可能会考虑只保存计数器的当前值,因为您可能想做一些除了使用格式化版本之外的事情。不过,这取决于您!


\xdef注意和之间的区别。首先,它们分别是和\protected@xdef的全局版本,因此这里的注释适用于这两种情况。TeX 基元/不断扩展\edef\protected@edef\edef\xdef一切在定义内部,直到它们达到“不可扩展”的材料。在原始层面上,这是有道理的,但使用 LaTeX 命令可能会出错。并非所有东西在完全展开时都“有意义”,实际上可能会导致错误,因为在扩展期间不会执行特定的分配。因此,LaTeX 提供了\protected@edef/ \protected@xdef,它们的设置使得各种 LaTeX 命令“变得安全”并且不会扩展。通常,最好使用\protected@edef/\protected@xdef来扩展 LaTeX 材料,而\edef/\xdef只有在您“完全控制”输入时才是真正安全的。

在当前情况下,显示计数器可能会从标准、可扩展发生变化 \alph,因此我非常谨慎。

相关内容