创建一个有条件地打开 \iffalse 和 \fi 的宏

创建一个有条件地打开 \iffalse 和 \fi 的宏

我想构造一个条件\iffalse\fi。具体来说,我想定义一个计数器和一个宏,这样,如果计数器设置为 1,则宏将设置一个\iffalse,如果宏的参数为“开”,或者\fi如果宏的参数为“关”)。类似的问题已在此主题这个但两者都涉及比我的简单结构更复杂的结构。

在下面的代码中,我定义了一个计数器Skip和一个宏\mySkip,如果正确定义,则如果\Skip设置为,将响应行并响应行1返回。如果设置为,则宏将被忽略。这样,我可以更改一行代码,从而显示或不显示大段文本。\iffalse\mySkip{on}\fi\mySkip{off}\Skip0

宏,而不是压制\mySkip{on}之间\mySkip{off}的文本推迟\mySkip{on}打印介于和之间\mySkip{off}直到 之后的文本\mySkip{\off}。有办法修复这个问题吗?

谢谢您的任何建议。

\documentclass{article}
\usepackage{pdftexcmds}
\usepackage{ifthen}
\makeatother
\newif\ifstrcmp
\makeatletter
\def\isstrcmp#1#2{
        \ifnum\pdf@strcmp{#1}{#2}=0
                \strcmptrue
        \else
                \strcmpfalse
        \fi
}
\makeatother
\newcounter{Skip}
\def\mySkip#1{\ifthenelse{\theSkip=1}{
        \isstrcmp{#1}{on}
        \ifstrcmp
            \iffalse
        \else
            \fi
            Text should resume after this\\
        \fi
        }{
        }}
\begin{document}
\setcounter{Skip}{1}
\mySkip{on}
This line should be skipped because it's between an ``iffalse'' and a ``fi''\\
In fact it's ``postponed" till immediately after the macro is ``closed"\\ \\
\mySkip{off}
This is the first line after the macro has been closed
\end{document}

答案1

无法通过宏模拟条件(,,,\iftrue),因为 TeX的扫描和\iffalse\else\fi\else\fi不是涉及宏扩展。条件命令只能分配给其他名称(通过\let)。起始条件可以隐藏在宏内,只要它不在另一个条件的分支内即可。

设置计数器的宏示例:

\documentclass{article}

\newcounter{Skip}% default is zero

\newcommand*{\mySkipOn}{%
  \setcounter{Skip}{1}%
  \iffalse
}
\newcommand*{\mySkipOff}{%
  \setcounter{Skip}{0}%
  \iftrue
}
\let\mySkipEnd=\fi

\begin{document}
\mySkipOn
This line should be skipped because it's between an \verb|\iffalse\verb|
and \verb|\fi|.\\
\mySkipEnd
This is the first line after the macro has been closed.
\end{document}

结果

警告:\mySkipOn/\mySkipOff不能嵌套,因为当 TeX 忽略未选择的条件分支的标记时,它不会将宏识别为条件。

使用计数器的宏示例:

\documentclass{article}

\newcounter{Skip}% default is zero

\newcommand*{\mySkipBegin}{%
  \ifnum\value{Skip}=1 %
    \csname iffalse\expandafter\endcsname
  \else
    \csname iftrue\expandafter\endcsname
  \fi
}
\let\mySkipEnd=\fi

\begin{document}
\setcounter{Skip}{1}
\mySkipBegin
This line should be skipped because it's between an \verb|\iffalse\verb|
and \verb|\fi|.\\
\mySkipEnd
This is the first line after the macro has been closed.
\end{document}

再次不应出现在其他条件分支内。其定义显示了如何通过外部分支\mySkipBegin隐藏不匹配的条件。命令结束外部条件分支,最后留下或。\csname\expandafter\iftrue\iffalse

答案2

分隔参数可能是你的朋友。

乌尔里希

\documentclass{minimal}

\makeatletter
%%-------------------------------------------------------------------------
%% Check whether argument is empty:
%%.........................................................................
%% \CheckWhetherNull{<Argument which is to be checked>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked is empty>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked is not empty>}%
\newcommand\@CheckWhetherNull[1]{%
  \expandafter\@secondoftwo\string{\expandafter\@secondoftwo
  \expandafter{\expandafter{\string#1}\expandafter\@secondoftwo
  \string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@firstoftwo\expandafter\@secondoftwo\expandafter}\string
  }\@firstoftwo
}%
%%-------------------------------------------------------------------------
%% Check whether argument contains no exclamation-mark on top-brace-level:
%%.........................................................................
%% \CheckWhetherNoExclamationMark{<Argument which is to be checked>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked does not contain !>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked does contain !>}%
\newcommand\@RemoveToExclamationMark{}%
\long\def\@RemoveToExclamationMark#1!{}%
\newcommand\@CheckWhetherNoExclamationMark[1]{%
  \expandafter\@CheckWhetherNull\expandafter{\@RemoveToExclamationMark#1!}%
}%
%%-------------------------------------------------------------------------
%% \mySkip{ON/TRUE/1} AND Skip = 1 -> Things until next \mySkip{...}
%%                                    within the same brace-level 
%%                                    will be skkipped.
%% !!The material to be skipped must not contain \outer tokens.!!
%%.........................................................................
\newcommand\@mySkipFork{}%
\long\def\@mySkipFork#1!!ON!TRUE!1!#2#3!!!!{#2}%
\newcommand\mySkip[1]{%
  \ifnum\number\value{Skip}=1 %
    \expandafter\@firstofone
  \else
    \expandafter\@gobble
  \fi
  {%
    \@CheckWhetherNoExclamationMark{#1}{%
      \uppercase{%
        \@mySkipFork
        !!#1!TRUE!1!{\removeToNextMySkip}%<-ON
        !!ON!#1!1!{\removeToNextMySkip}%<-TRUE
        !!ON!TRUE!#1!{\removeToNextMySkip}%<-1
        !!ON!TRUE!1!{}%<-ELSE
        !!!!%
      }%
    }{}%<-ELSE
  }%
  \ignorespaces
}%
%
\newcommand\removeToNextMySkip{}%
\long\def\removeToNextMySkip#1\mySkip#2{\ignorespaces}%
%
\newcounter{Skip}%
%
\makeatother


\begin{document}
\setcounter{Skip}{1}
This is the first phrase that should definitely not be skipped.
\mySkip{oN}
This phrase should probably be skipped.
\mySkip{off}
This is the second phrase that should definitely not be skipped.
\end{document}

答案3

以下方法允许跳过\outer标记并跳过诸如逐字环境实例之类的内容。请注意,您不能隐藏\mySkip{...}在其他宏中或宏的参数中,并且\mySkip不应“重命名”(通过\let)。

匹配\mySkip{...}对不能来自宏扩展,但它们必须出现在同一个 .tex 输入文件中。

这次,代码中没有任何宏\if代码内的..开关。

为了避免浪费\count寄存器作为跳过计数器,\Skipflag我们发明了一个简单的宏。如果该宏未定义为扩展为短语“1”,则不会跳过任何内容。

乌尔里希

\documentclass{minimal}
\usepackage{verbatim}

\makeatletter
%%-------------------------------------------------------------------------
%% Check whether argument is empty:
%%.........................................................................
%% \CheckWhetherNull{<Argument which is to be checked>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked is empty>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked is not empty>}%
\newcommand\@CheckWhetherNull[1]{%
  \expandafter\@secondoftwo\string{\expandafter\@secondoftwo
  \expandafter{\expandafter{\string#1}\expandafter\@secondoftwo
  \string}\expandafter\@firstoftwo\expandafter{\expandafter
  \@firstoftwo\expandafter\@secondoftwo\expandafter}\string
  }\@firstoftwo
}%
%%-------------------------------------------------------------------------
%% Check whether argument contains no exclamation-mark on top-brace-level:
%%.........................................................................
%% \CheckWhetherNoExclamationMark{<Argument which is to be checked>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked does not contain !>}%
%%                  {<Tokens to be delivered in case that
%%                    argument which is to be checked does contain !>}%
\newcommand\@RemoveToExclamationMark{}%
\long\def\@RemoveToExclamationMark#1!{}%
\newcommand\@CheckWhetherNoExclamationMark[1]{%
  \expandafter\@CheckWhetherNull\expandafter{\@RemoveToExclamationMark#1!}%
}%
%%-------------------------------------------------------------------------
%% \mySkip{ON/TRUE/1} AND \Skipflag = 1 -> Things until next \mySkip{...}
%%                                         will be skkipped.
%%.........................................................................
\newcommand\@mySkipFork{}%
\long\def\@mySkipFork#1!!1.ON!1.TRUE!1.1!#2#3!!!!{#2}%
\newcommand\mySkip[1]{%
  \expandafter\@mySkip\expandafter{\number0\Skipflag.#1}%
}%
\newcommand\@mySkip[1]{%
    \@CheckWhetherNoExclamationMark{#1}{%
      \uppercase{%
        \@mySkipFork
        !!#1!1.TRUE!1.1!{\removeToNextMySkip}%<-ON
        !!1.ON!#1!1.1!{\removeToNextMySkip}%<-TRUE
        !!1.ON!1.TRUE!#1!{\removeToNextMySkip}%<-1
        !!1.ON!1.TRUE!1.1!{}%<-ELSE
        !!!!%
      }%
    }{}%<-ELSE
  \ignorespaces
}%
%
\newcommand\removeToNextMySkip{%
  \begingroup
  \let\do\@makeother
  \dospecials
  \@removeToNextMySkip
}%
\begingroup
\newcommand\@removeToNextMySkip{%
   \endgroup
   \begingroup
   \newcommand\@removeToNextMySkip[1]{%
     \endgroup
     \long\def\@removeToNextMySkip####1##1{%
       \endgroup\@firstoftwo{\ignorespaces}%
     }%
   }%
   \let\do\@makeother
   \dospecials
   \catcode`\{=1 %
   \catcode`\}=2 %
   \@removeToNextMySkip%
}%
\@removeToNextMySkip{\mySkip}%
%
\makeatother

\outer\def\outerphrase{%
  This phrase comes from an outer-deffed token and it should
  probably be skipped as well. %
}%


\begin{document}
\newcommand\Skipflag{1}%
%\renewcommand\Skipflag{0}%
This is the first phrase that should definitely not be skipped.
\mySkip{oN}
This phrase should probably be skipped.
\outerphrase
\begin{verbatim}
And this is some verbatim stuff that also should probably be skipped.
\end{verbatim}
\mySkip{off}
This is the second phrase that should definitely not be skipped.
\end{document}

相关内容