扩展问题:如何检查传递给环境的选项是否被使用

扩展问题:如何检查传递给环境的选项是否被使用

我有一个环境,其中部分内容根据传递给环境的选项进行有条件排版。此环境的内容在其他地方已预定义,因此根据选项会产生不同的输出。这部分代码运行良好。

现在,我尝试在环境结束时报告错误,如果指定了无法识别的选项来检测任何可能被忽视的拼写错误,并且遇到了看起来像是扩展问题的问题。我尝试了所有可能对我有意义的\edef和用法\expandafter,但没有找到正确的组合。

我这样做是为了存储所有指定选项的列表,最后检查\<optionName>Encountered每个指定选项的控制序列是否已定义。在环境主体中,我定义:

\global\expandafter\def\csname <optionName> Encountered\endcsname{}%

对于<optionName>可能已经指定的每个可能,并根据指定的选项列表进行检查。

笔记:

  • 选项列表中还有其他选项不适用此类检查,因此我需要将它们单独添加到列表中,而不是仅仅存储#1\foreach在最后对该值执行循环。
  • 我希望选项本身存储在列表中(而不是它们对应的 csname),因为这样我可以在环境结束时更好地报告错误消息。
  • 此外,也可以在宏中定义选项。

参考:

预期输出:

当代码\ReportAnyUnusedOptions取消注释并且一切正常时,应该会产生:

enter image description here

代码:

问题代码\ReportAnyUnusedOptions已被注释,以允许编译。

\documentclass{article}
\usepackage{pgffor}

\def\ListOfOptions{}

\makeatletter
\newcommand{\AddToListOfOptions}[1]{%
    \g@addto@macro\ListOfOptions{#1}%
    \typeout{***** Added option to check: #1}%
}
\makeatother


\newcommand{\ReportAnyUnusedOptions}{%
    Checking options: \par%
        \foreach \y in \ListOfOptions {%
%           \y%
%           \ifcsname\y Encountered\endcsname%
%               ~Encountered!\par%
%           \else%
%               ~Not Encountered!\par%
%           \fi%
        }%
}%

\newenvironment{ApplyOptions}[1]{%
   % Add to list of options to later check that they were used
    \foreach \x in {#1} {%
        % There are cases here where an option would not be
        % check later, so some will be skipped
        \edef\Option{\x}%
        \AddToListOfOptions{\Option}%
    }%
}{%
    \ReportAnyUnusedOptions%
}


\begin{document}
\begin{ApplyOptions}{Option A, Option C}%
    % Option A:
    % if "Option A" was specified, it would be typset here
    \global\expandafter\def\csname Option A Encountered\endcsname{}%
    %
    % Option B:
    % if "Option B" was specified, it would be typset here
    \global\expandafter\def\csname Option B Encountered\endcsname{}%
\end{ApplyOptions}

% Also possible to specify the options via a macro
\newcommand*{\MyOptions}{Option AA, Option CC}%
\begin{ApplyOptions}{\MyOptions}%
    % Option A:
    % if "Option AA" was specified, it would be typset here
    \global\expandafter\def\csname Option AA Encountered\endcsname{}%
    %
    % Option B:
    % if "Option BB" was specified, it would be typset here
    \global\expandafter\def\csname Option BB Encountered\endcsname{}%
\end{ApplyOptions}
\end{document}

答案1

这是一个expl3版本:

\documentclass{article}
\usepackage{xparse}

\ExplSyntaxOn

\seq_new:N \g_optchk_options_seq

\cs_new_protected:Npn \optchk_report: 
 {
  Checking~options: \par
  \seq_map_inline:Nn \g_optchk_options_seq
   {
    ##1 ~ \cs_if_exist:cF { ##1 ~ Encountered } {~ Not} ~ Encountered \par
   }
 }
\cs_new_protected:Npn \optchk_add:n #1
 {
  \seq_gclear:N \g_optchk_options_seq
  % Add to list of options to later check that they were used
  \seq_gset_split:Nno \g_optchk_options_seq { , } { #1 }
  \seq_map_inline:Nn \g_optchk_options_seq
   { \typeout{*****~Added~option~to~check:~##1} }
 }
 \cs_generate_variant:Nn \seq_gset_split:Nnn {Nno}

\NewDocumentEnvironment{ApplyOptions}{ m }
 { \optchk_add:n { #1 } }
 { \optchk_report: }

\ExplSyntaxOff

\begin{document}
\begin{ApplyOptions}{Option A, Option C}%
    % Option A:
    % if "Option A" was specified, it would be typset here
    \expandafter\gdef\csname Option A Encountered\endcsname{}%
    %
    % Option B:
    % if "Option B" was specified, it would be typset here
    \expandafter\gdef\csname Option B Encountered\endcsname{}%
\end{ApplyOptions}
\end{document}

该方法的局限性\foreach在于,所有循环都在不同的组中执行。使用序列(或列表)的方法允许在需要时选择全局分配,并使管理更加容易。

这里我们定义了环境以将指定的选项添加到序列中(并且我们没有定界问题)。环境的“结束部分”使用\ReportAnyUnusedOptions不同的名称。

注意:我已将\seq_gset_split:Nnn其改为\seq_gset_split:Nno,以便传递给环境的参数被扩展一次。根据选项的性质,人们可能更喜欢使用\seq_gset_split:Nnx(生成适当的变体),但如果选项本身包含宏,这可能是危险的。

答案2

enter image description here

您的选项列表并不像您想象的那样。在两个选项之后,它被定义为

\ListOfOptions=macro:
->\Option \Option .

即,它仅告诉您选项的数量,而不是选项的数量。

所以改变它

> \ListOfOptions=macro:
->\Option A Encountered \Option C Encountered .

然后:

\documentclass{article}
\usepackage{pgffor}

\def\ListOfOptions{}

\makeatletter
\newcommand{\AddToListOfOptions}[1]{%
    \expandafter\g@addto@macro\expandafter\ListOfOptions \csname #1 Encountered\endcsname
    \typeout{***** Added option to check: #1}%
}


\def\foo#1>{}

\newcommand{\ReportAnyUnusedOptions}{%
    Checking options: \par%
    \def\x{\@tfor\y:=}%
       \expandafter\x\ListOfOptions\do{%
           \expandafter\show\y\relax
           \expandafter\ifx\y\@undefined
            \expandafter\foo\meaning\y!\par%
           \else%
            NOT \expandafter\foo\meaning\y!\par%
           \fi}%
}%

\newenvironment{ApplyOptions}[1]{%
   % Add to list of options to later check that they were used
    \foreach \x in {#1} {%
        % There are cases here where an option would not be
        % check later, so some will be skipped
        \edef\Option{\x}%
        \AddToListOfOptions{\Option}%
    }%
}{%
    \ReportAnyUnusedOptions%
}

\makeatother
\begin{document}
\begin{ApplyOptions}{Option A, Option C}%
    % Option A:
    % if "Option A" was specified, it would be typset here
    \global\expandafter\def\csname Option A Encountered\endcsname{}%
    %
    % Option B:
    % if "Option B" was specified, it would be typset here
    \global\expandafter\def\csname Option B Encountered\endcsname{}%
\end{ApplyOptions}
\end{document}

答案3

我想让 Peter 在每个环境调用中节省一个完整的循环。这就是我发布此解决方案的原因。此外,\cptdocommalist规范化列表。

\documentclass{article}
\usepackage{catoptions}
\makeatletter
\newcommand{\optionsnotused}{%
  Checking options:
  \def\do##1##2{\endgraf{\tt % You can remove \tt if you like.
    \ifdefTF{##2}{%
      Option `##1' used: \expandafter\strip@prefix\meaning##2%
      \undefcs{##2}% remove any \gdef'ed temporary commands.
    }{%
      Option `##1' not used%
    }%
  }}%
  \listofoptions
}
\newenvironment{applyoptions}[1]{%
  % \listofoptions is re-initialized by the environment. I am not sure 
  % if this is what Peter intended. If not, take '\def\listofoptions{}'
  % outside the environment definition:
  \def\listofoptions{}%
  \ifmacroTF{#1}{\let\cpt@tempa#1}{\def\cpt@tempa{#1}}%
  \cptdocommalist*\cpt@tempa{%
    \edef\option{##1}% Does Peter really need to define \option here?
    \xdef\listofoptions{\expandcsonce\listofoptions
      \unexpanded{\do{##1}}{\noexpandcsn{##1-used}}}%
    \typeout{Option '\detokenize{##1}' added, to check at end of environment.}%
  }%
}{%
  \optionsnotused
  \ignorespacesafterend
}
\makeatother
\begin{document}
\begin{applyoptions}{Option A, Option B}
  % If 'Option A' was specified, it would be typeset here.
  % Let us simulate its use here:
  \csngdef*{Option A-used}{Used here by Peter Gill}%
  % Let's assume that 'Option B' wasn't used here.
\end{applyoptions}
\end{document}

相关内容