宏扩展困难

宏扩展困难

我没有从我的 LaTeX 代码中获得我想要的输出,我认为问题在于我没有在正确的时间扩展事物。

代码

\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}

解释

第一个 foreach 循环遍历一串对,构建一个从标识符(例如 、 等)到数字的函数ab此函数的值存储在一系列宏中(例如,\wickerson@a@pos存储标识符的函数值a),此函数的域是逗号分隔的列表\@activeLines。最初\@activeLines为空,最后为a,b,c,d

我在每次循环后打印我的函数。最后我希望打印

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

但它打印的是

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

我怀疑问题在于当我向函数添加映射时,我没有充分扩展值,因此当我添加另一个映射时它会发生变化。我以为使用ExpandNextTwofrometextools可以很好地扩展所有内容,但似乎没有帮助。有什么想法吗?

第二个 foreach 循环尝试在命令的帮助下从我的函数中删除一些映射\@removeelement。不幸的是,它不起作用。映射仅在当前循环迭代的范围内被删除,并且在下一次迭代时,映射已返回。我怀疑问题是\@removeelement没有\@activeLines全局重新定义,但我不知道如何解决这个问题。有什么想法吗?(我希望我不需要切换到语法expl3来实现这一点,因为这将需要对我的代码进行相当大的更改!)

电流输出

在此处输入图片描述

期望输出

在此处输入图片描述

答案1

以下两个宏需要修复:

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

这将定义为\@defineLine{a}{10}\wickerson@#1@pos而不是\wickerson@a@pos。修复:

\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}

由于\xdef扩展,因此使用宏名称的一部分而不是参数\csname来执行。修复:\wickerson@#1@pos#1

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

删除最后一个逗号

的一个特性\foreach可以用来删除输出中最新的逗号:

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

结果

移除

该问题使用以下定义来删除元素:

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

确实,LaTeX 的效果\@removeelement仅限于当前组。如果你想进行全局更改\@activeLines,则\global\let有帮助:

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

答案2

我相信它expl3更直接,因为它不依赖于了解需要扩展什么以及何时扩展。

\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}

在此处输入图片描述


一种不同的实现,可以删除状态;我既使用了“斜线前”部分的序列,也使用了包含值的属性列表(“斜线后”)。

该命令\defineStates重新初始化变量,同时\addStates将新状态放入变量中;同样,\removeStates删除状态。最后,\printStates仅给出状态的可视化表示。

\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}

在此处输入图片描述

相关内容