条件在 TeX 中如何扩展?

条件在 TeX 中如何扩展?

我正在排版一系列数学问题、解决方案和注释纯 TeX。简化文件为:

\def\problem{Problem. }
\def\solution{Solution. }
\def\note{Note. }

\problem (The first problem)

\solution (Solution to the first problem)

\note (Note to the first problem)

\problem (The second problem)

\problem (The third problem)

\solution (Solution to the third problem)

现在我想排版文档没有解决方案和注释,只修改了文档的第一行和最后一行(因为问题比较多)。

我注意到,它\iffalse可以用来“注释掉”内容块,所以我

\newif\ifproblemonly\problemonlytrue
\def\problem{Problem. \ifproblemonly\iffalse\fi}

但是,这不能编译(原因很明显: 与\fi一起\iffalse,而不是 与 一起\ifproblemonly)。我注意到,添加一对花括号(我不确定是否{}这样称呼)可以解决问题。

当我再次重写\problem以添加\fi(与一起\iffalse)时,我添加了一个新的计数,并且得到了如下内容:

\newif\ifproblemonly\problemonlytrue
\newcount\isproblem
\def\problem{\ifproblemonly{\if\the\isproblem0{\fi}\fi}\fi Problem. \ifproblemonly{\iffalse}\fi \isproblem 1\relax}
\def\solution{Solution. \isproblem 0\relax}
\def\note{Note. \isproblem 0\relax}

并添加\ifproblemonly{\if\the\isproblem0{\fi}\fi}\fi于文档末尾。

\fi然而,当我尝试编译它时遇到错误“Extra ”。

我的问题是:

  • 这里有什么问题?
  • 有没有更好的(在某些方面)方法来“注释掉”这些解决方案和注释?
  • TeX 中如何处理条件语句(\if、、等)的扩展?\ifx\ifnum

提前致谢。

答案1

我建议将问题部分(我猜除了它们之外您可能还有其他文本)附在里面\problems...\endproblems

\newif\ifsolutionsonly
\newif\ifskipping
\newbox\skipbox

\def\problems{%
  \ifsolutionsonly
    \def\problem{%
      \ifskipping\egroup\fi
      \par Problem. \ignorespaces
    }%
    \def\solution{\ifskipping\else\setbox\skipbox=\vbox\bgroup\skippingtrue\fi}
    \let\note\solution
  \else
    \def\problem{Problem. \ignorespaces}
    \def\solution{Solution. \ignorespaces}
    \def\note{Note. \ignorespaces}
  \fi
}
\def\endproblems{\ifskipping\egroup\fi}


\problems
\problem (The first problem)

\solution (Solution to the first problem)

\note (Note to the first problem)

\problem (The second problem)

\problem (The third problem)

\solution (Solution to the third problem)
\endproblems

\bigskip

\solutionsonlytrue

\problems
\problem (The first problem)

\solution (Solution to the first problem)

\note (Note to the first problem)

\problem (The second problem)

\problem (The third problem)

\solution (Solution to the third problem)
\endproblems

\bye

如果\ifsolutionsonly返回 true,\solution则将\note开始在将被丢弃的框中排版,而\problem将停止正在构建的框(并且它将被丢弃)。最后\endproblems将处理可能仍在排版的框。

在此处输入图片描述

另一方面,语法如下

\problem
(The first problem)
\endproblem

\solution
(Solution to the first problem)
\endsolution

\note
(Note to the first problem)
\endnote

\problem
(The second problem)
\endproblem

\problem
(The third problem)
\endproblem

\solution
(Solution to the third problem)
\endsolution

将会更加容易管理。

答案2

只要文件以 结尾,下面的方法就可以工作\bye。它通过设置\solution\note来吞噬所有内容,直到下一次调用\problem\endgobble(不产生输出的特殊标记,仅为方便而提供)或\bye。虽然这不会特别快,但不需要对文件进行任何更改(除了可能添加\bye到末尾)。

\newif\ifproblemonly\problemonlytrue

\def\problem{Problem. }
\ifproblemonly
  \def\solution{\gobblesolutionandnote}
  \def\note{\gobblesolutionandnote}
\else
  \def\solution{Solution. }
  \def\note{Note. }
\fi

\def\endgobble{\gobble{\endgobble}}% <- arbitrary end flag that doesn't produce output
\long\def\firstofone#1{#1}
\long\def\gobbletwo#1#2{}
\long\def\gobble#1{}
\long\def\ifnextnot#1{\ifx\next#1\expandafter\gobbletwo\fi\firstofone}
\long\def\ifnextnotbye{\expandafter\ifx\csname bye\endcsname\next\expandafter\gobbletwo\fi\firstofone}
\def\gobblesolutionandnote
  {%
    \afterassignment\gobblesolutionandnoteAUX
    \let\next=
  }
\def\gobblesolutionandnoteAUX
  {%
    \ifnextnot\problem
      {%
        \ifnextnot\endgobble
          {\ifnextnotbye{\expandafter\gobblesolutionandnote\gobble}}%
      }%
    \next
  }

\problem (The first problem)

\solution (Solution to the first problem)

\note (Note to the first problem)

\problem (The second problem)

\problem (The third problem)

\solution (Solution to the third problem)

\bye

答案3

我不会回答有关\if...\fi工作原理的问题,而是会以不同的方式处理这个问题。通过启用行之间的替代定义%,解决方案和注释将被排除。

\def\problem#1{Problem. #1}
\def\solution#1{Solution. #1}
\def\note#1{Note. #1}
%
\def\solution#1{}
\def\note#1{}
%

\problem {(The first problem)}

\solution {(Solution to the first problem)}

\note {(Note to the first problem)}

\problem {(The second problem)}

\problem {(The third problem)}

\solution {(Solution to the third problem)}

\bye

在此处输入图片描述

如果我注释掉两行重新定义,我会得到完整的结果:

在此处输入图片描述

答案4

我可以提供一个宏\GobbleTillProblemOrContinueOrBye,在本地范围内将 catcode{和切换}为 12,并使\bye非外部的和 - 如果\meaning\everyeof产生字符串\everyeof并因此表示现有的原语 - 设置\everyeof{\continue}然后调用一个宏\Gobbleloop,该宏以尾部递归的方式吞噬标记,直到当前处理的标记是控制字标记\problem或控制字标记\continue或控制字标记,\bye在这种情况下,本地范围关闭并且所讨论的标记被传递。

如果您只想打印问题,您可以使用命令\solution并调用此宏。\note

请注意,内部实现没有任何条件,但有提问者请求的\if..切换。\ifproblemonly

意识到:

  • \input-如果仅要打印问题,则不会执行注释/解决方案中出现的命令。

  • \endinput-如果仅要打印问题,则注释/解决方案中出现的命令也不会执行。

  • 对于 e-TeX 扩展/\everyeof不可用,\Gobbleloop则不会检查是否到达文件末尾。

因此:使用引擎处理的文件\everyeof并且不以控制字标记结尾,\bye应该以控制字标记结尾\continue,可能后面跟着\endinput或您用来终止文件/ .tex 文档处理的任何命令。

否则,如果只打印问题,则例程\Gobbleloop吞噬事物的例程可能会传递有关文件意外结束的错误消息。

%%=============================================================================
%% Toggle: problem only -- also solutions/notes
%%.............................................................................
\newif\ifproblemonly
\problemonlytrue
%\problemonlyfalse
%%=============================================================================
%% PARAPHERNALIA:
%% \UDfirstoftwo, \UDsecondoftwo, \UDExchange, \UDstopromannumeral, 
%% \UDgobbletoexclam, \UDCheckWhetherNull,
%%-----------------------------------------------------------------------------
\long\def\UDfirstoftwo#1#2{#1}%
\long\def\UDsecondoftwo#1#2{#2}%
\long\def\UDExchange#1#2{#2#1}%
\chardef\UDstopromannumeral=`\^^00%
\long\def\UDgobbletoexclam#1!{}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UDCheckWhetherNull{<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>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\long\def\UDCheckWhetherNull#1{%
  \romannumeral\expandafter\UDsecondoftwo\string{\expandafter
  \UDsecondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UDsecondoftwo\string}\expandafter\UDfirstoftwo\expandafter{\expandafter
  \UDsecondoftwo\string}\expandafter\UDstopromannumeral\UDsecondoftwo}{%
  \expandafter\UDstopromannumeral\UDfirstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% \Gobbleloop
%%.............................................................................
\let\savedbye=\bye
\edef\bye{\noexpand\bye}%
\def\neutralizebye{\edef\bye{\noexpand\bye}}%
\long\def\Gobbleloop#1{%
  \expandafter\UDCheckWhetherNull\expandafter{\UDgobbletoexclam#1!}%
  {%
    \ByeContinueProblemFork
    !#1!\continue!\problem!{\endgroup#1}%
    !\bye!#1!\problem!{\endgroup#1}%
    !\bye!\continue!#1!{\endgroup#1}%
    !\bye!\continue!\problem!{\Gobbleloop}%
    !!!!%
  }{\Gobbleloop}%
}%
\long\def\ByeContinueProblemFork#1!\bye!\continue!\problem!#2#3!!!!{#2}%
%%.............................................................................
%% \everyeoffork is needed to check the meaning of \everyeof
%%.............................................................................
\expandafter\def\expandafter\everyeoffork\expandafter#%
\expandafter1\expandafter$\string\everyeof$#2#3$$${#2}%
%%.............................................................................
%% Macro to initiate \Gobbleloop depending on \if...-switch:
%%.............................................................................
\def\GobbleTillProblemOrContinueOrBye#1{%
  \ifproblemonly
    \begingroup\catcode`\{=12 \catcode`\}=12 \neutralizebye
    %%.........................................................................
    % In case \everyeof is defined to be the \everyeof-primitive, i.e., in
    % case \meaning\everyeof yields the string \everyeof, do
    % \everyeof{\continue} for handling the case of reaching the end of a file
    \expandafter\UDExchange\expandafter{\string\everyeof${}}%
    {\expandafter\everyeoffork\expandafter$\meaning\everyeof$%
     {\everyeof{\continue}}$}$$$%
    %%.........................................................................
    \expandafter\Gobbleloop
  \else#1. \fi
}%
\let\bye=\savedbye
\let\savedbye\UnDeFineD
%%=============================================================================
\def\problem{Problem. }%
\def\solution{\GobbleTillProblemOrContinueOrBye{Solution}}%
\def\note{\GobbleTillProblemOrContinueOrBye{Note}}%
\def\continue{}%
%%=============================================================================

\problem  
(The first problem)

\solution (Solution to the first problem)

\note (Note to the first problem)

\problem (The second problem)

\problem (The third problem)

\solution (Solution to the third problem)

\continue (something that shall occur in any case although it is not a problem)

\note (Note to the third problem)

\problem % some comment
(The fourth problem)

\solution (Solution to the fourth problem)

\bye

这就是您得到的\problemonlyfalse

在此处输入图片描述

以下是您可以获得的\problemonlytrue

在此处输入图片描述



如果你喜欢真正复杂的代码,不仅适用于检测单个控制字标记,还适用于检测由多个标记组成的短语,我可以提供一个例程,\GobbleTillProblemOrSolutionOrNoteOrContinueOrBye该例程切换到逐字猫码制度,并通过累积字符和通过分隔参数检查吞噬所有内容,直到找到其中一个短语/ /\problem⟨space⟩\problem⟨carriage return⟩\continue⟨space⟩ / / / 。\continue⟨carriage return⟩\bye⟨space⟩\bye⟨carriage return⟩

!!! 尾随/分离⟨空间⟩或者⟨回车⟩必須存在!!!

例如,\continue\relax不作为终止 gobbling-loop 的标记。
例如\continue%,不作为终止 gobbling-loop 的标记。
但是,对于 ,即对于,序列 将被视为终止 gobbling-loop 的标记。\continue⟨space⟩\relax\continue \relax\continue⟨space⟩

如果您只想打印问题,您可以使用该命令\solution或调用此例程。\note

只有在出现打印问题时,您才可以忽略对/和/的扫描,因为在这种情况下,这些只会引发更多的吞噬。 我决定添加对这些注释掉的扫描,以便您可以更轻松地引入更多-switch,例如,用于打印注释但不打印解决方案,或者用于打印解决方案但不打印注释,而这些都需要扫描。\solution⟨space⟩\solution⟨carriage return⟩\note⟨space⟩\note⟨carriage return⟩
\if

%%=============================================================================
%% Toggle: problem only -- also solutions/notes
%%.............................................................................
\newif\ifproblemonly
\problemonlytrue
%\problemonlyfalse
%%=============================================================================
%% PARAPHERNALIA:
%% \UDfirstoftwo, \UDsecondoftwo, \UDExchange, \UDstopromannumeral, 
%% \UDgobbletoexclam, \UDCheckWhetherNull,
%%-----------------------------------------------------------------------------
\long\def\UDfirstoftwo#1#2{#1}%
\long\def\UDsecondoftwo#1#2{#2}%
\long\def\UDExchange#1#2{#2#1}%
\chardef\UDstopromannumeral=`\^^00%
\long\def\UDgobbletoexclam#1!{}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UDCheckWhetherNull{<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>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\long\def\UDCheckWhetherNull#1{%
  \romannumeral\expandafter\UDsecondoftwo\string{\expandafter
  \UDsecondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UDsecondoftwo\string}\expandafter\UDfirstoftwo\expandafter{\expandafter
  \UDsecondoftwo\string}\expandafter\UDstopromannumeral\UDsecondoftwo}{%
  \expandafter\UDstopromannumeral\UDfirstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Scratch-macros for defining final version of \Gobbleloop
%%.............................................................................
\def\Gobbleloop#1#2#3{%
  % #1: definition-text/(sub-)phrase-checks of final \Gobbleloop
  %     accumulated so far.
  % #2: <phrase> whose matching-check is to be defined and 
  %     appended to #1 this time.
  % #3: emptiness or tokens to carry out in case characters
  %     gathered match <phrase>.
  \UDCheckWhetherNull{#2}{\def\Gobbleloop##1##2{#1}}{%
      \csname\string\fork\expandafter\endcsname
      \csname#2fork\expandafter\endcsname
      \csname fork#2\endcsname
      {#3}{#2}{#1}%
  }%
}%
\expandafter\def\csname\string\fork\expandafter\endcsname#1#2#3#4#5{%
  % #1: \<phrase>fork - 1st macro-token of mechanism forming 
  %     matching-check for <phrase>
  % #2: \fork<phrase> - 2nd macro-token of mechanism forming 
  %     matching-check for <phrase>
  % #3: emptiness or tokens to carry out in case characters
  %     gathered match <phrase>.
  % #4: <phrase> whose matching-check is to be defined and 
  %     appended to #1 this time.
  % #5: definition-text/(sub-)phrase-checks of final \Gobbleloop
  %     accumulated so far.
  \def#1##1{%
    \expandafter\UDCheckWhetherNull\expandafter{\UDgobbletoexclam##1!}%
    {#2!##1!{\UDfirstoftwo}!#4!{\UDsecondoftwo}!!!!}{\UDsecondoftwo}%
  }\long\def#2##1!#4!##2##3!!!!{##2}%
  \UDCheckWhetherNull{#3}{%
    \Gobbleloop{#1{##1##2}{\Gobbleloop{#4}}{#5}}%
  }{%
    \Gobbleloop{#1{##1##2}{\endgroup#3}{#5}}%
  }%
}%
%%.............................................................................
%% \everyeoffork is needed to check the meaning of \everyeof
%%.............................................................................
\expandafter\def\expandafter\everyeoffork\expandafter#%
\expandafter1\expandafter$\string\everyeof$#2#3$$${#2}%
%%.............................................................................
%% Change the catcode-régime for defining final version of \Gobbleloop
%% and call the scratch-\Gobbleloop for defining the final \Gobbleloop
%%.............................................................................
\catcode`\/=0\relax%
\catcode`\^^M=12\relax%
\catcode`\ =12\relax%
\catcode`\!=14\relax%
\catcode`\%=12\relax!
\catcode`\\=12/relax!
/def/continuephrase{\continue }!
!! The scratch-\Gobbleloop recursively iterates over 2-tuples of arguments
!! for defining the final \Gobbleloop.
!! The 1st component of such a 2-tuple s the character-sequence forming the
!!    (sub-)phrase to be matched.
!! The 2nd component is the tokens to carry out after terminating the loop
!!    in case of match.
!! The final \Gobbleloop recursively gathers characters one by one from
!!    the input and compares the characters gathered in previous iterations 
!!    plus the character gathered in this iteration to each (sub-)phrase.
!! If a match occurs and with that match the second component of the
!!    corresponding 2-tuple is not empty, the loop terminates and delivers 
!!    the tokens comeing from the 2nd component of the corresponding 2-tuple.
!! If a match occurs and with that match the second component of the
!!    corresponding 2-tuple is empty, the loop continues with the matching
!!    phrase forming the characters gathered so far.
!! If no match occurs, the loop continues with characters gathered so far
!!    being empty.
/Gobbleloop{/Gobbleloop{}}!
!..............................................................................
! If you want \Gobbleloop to terminate when finding \note<return> or 
! \note<space> and to call \note:
!{\note^^M}{note}{\note }{note}{\note}{}{\not}{}{\no}{}{\n}{}!
!..............................................................................
! If you want \Gobbleloop to terminate when finding \solution<return> or
! \solution<space> and to call \solution:
!{\solution^^M}{/solution}{\solution }{/solution}!
!{\solution}{}{\solutio}{}{\soluti}{}{\solut}{}{\solu}{}{\sol}{}{\so}{}{\s}{}!
!..............................................................................
! If you want \Gobbleloop to terminate when finding \bye<return> or 
! \bye<space> and to call \bye:
{\bye^^M}{/csname/UDfirstoftwo{}{}bye/endcsname}!
{\bye }{/csname/UDfirstoftwo{}{}bye/endcsname}{\bye}{}{\by}{}{\b}{}!
!..............................................................................
! If you want \Gobbleloop to terminate when finding \continue<return> or
! \continue<space> and to call \continue:
{\continue^^M}{/continue}{\continue }{/continue}!
{\continue}{}{\continu}{}{\contin}{}{\conti}{}{\cont}{}{\con}{}{\co}{}{\c}{}!
!..............................................................................
! If you want \Gobbleloop to terminate when finding \problem<return> or
! \problem<space> and to call \problem
{\problem^^M}{/problem}{\problem }{/problem}{\problem}{}!
{\proble}{}{\probl}{}{\prob}{}{\pro}{}{\pr}{}{\p}{}!
!..............................................................................
! Scan for the backslash:
{\}{}!
! ( When scanning for more (sub-)phrases you need to add tuples 
!   only for those (sub-)phrases that are not already in the
!   2-tuple-list. )
! Scanning for the backslash must be the last item of the 2-tuple
! list before the terminating-tuple because this overrides the
! scratch-definition of the \\fork-macro.
!
! This is the tuple terminating the recursion of the scratch-\Gobbleloop -
! emptiness as sub-phrase to match would not make sense:
{}{}!
!!.............................................................................
!! Reset the catcode-régime:
!!.............................................................................
/catcode`/\=0/relax!
\catcode`\%=14\relax!
\catcode`\!=12\relax%
\catcode`\ =10\relax%
\catcode`\^^M=5\relax%
\catcode`\/=12\relax%
%%.............................................................................
%% Macro to switch catcode-régime and to call \Gobbleloop
%%.............................................................................
\def\InitiateGobbleloop{%
  \begingroup
  \catcode`\^^M=12 \catcode`\ =12 \catcode`\\=12 \catcode`\{=12 \catcode`\}=12 %
  \catcode`\$=12 \catcode`\&=12 \catcode`\#=12 \catcode`\^=12 \catcode`\_=12 %
  \catcode`\~=12 % \catcode`\%=12 %
  %%.........................................................................
  % In case \everyeof is defined to be the \everyeof-primitive, i.e., in
  % case \meaning\everyeof yields the string \everyeof, do
  % \everyeof{\continue} for handling the case of reaching the end of a file
  \expandafter\UDExchange\expandafter{\string\everyeof${}}%
  {\expandafter\everyeoffork\expandafter$\meaning\everyeof$%
   {\everyeof\expandafter{\continuephrase}}$}$$$%
  %%.........................................................................
  \Gobbleloop{}%
}%
%%.............................................................................
%% Macro to initiate \Gobbleloop depending on \if...-switch:
%%.............................................................................
\def\GobbleTillProblemOrSolutionOrNoteOrContinueOrBye#1{%
  \ifproblemonly
    \expandafter\InitiateGobbleloop
  \else
    #1. \ignorespaces\expandafter\noexpand
  \fi
}%
%%=============================================================================
\def\problem{Problem. \ignorespaces\noexpand}%
\def\solution{\GobbleTillProblemOrSolutionOrNoteOrContinueOrBye{Solution}}%
\def\note{\GobbleTillProblemOrSolutionOrNoteOrContinueOrBye{Note}}%
\def\continue{\ignorespaces\noexpand}%
%%=============================================================================

\problem  
(The first problem)

\solution (Solution to the first problem)

\note (Note to the first problem)

\problem (The second problem)

\problem (The third problem)

\solution (Solution to the third problem)

\continue (something that shall occur in any case although it is not a problem)

\note (Note to the third problem)

\problem % some comment
(The fourth problem)

\solution (Solution to the fourth problem)

\bye

以下是您可以获得的\problemonlyfalse

在此处输入图片描述

以下是您可以获得的\problemonlytrue

在此处输入图片描述

请注意,内部实现没有任何条件,但有提问者请求的\if..切换。\ifproblemonly



通过这两个示例,您可以轻松修改代码,以便在找到\input或 时, gobbling-routine 也会停止\endinput

如果您这样做,我建议通过/和/复制\input和,其中不会停止吞噬,以便只有当不仅产生问题而且产生解决方案/注释时才执行这些操作。这些可用于输入其全部内容属于解决方案或注释的文件,仅在出现相应的-/ -命令时才使用。\endinput\let\noteinput=\input\let\solutioninput=\input\let\noteendinput=\endinput\let\solutionendinput=\endinput\noteinput\solutioninput

相关内容