如何在空格后使用 \futurelet 标记

如何在空格后使用 \futurelet 标记

假设我们要处理作为参数给出的标记列表,并且要在\futurelet处理之前检查每个标记。如何识别\futurelet空格后的标记?

请考虑以下示例:

\def\process#1{\doA#1}
\def\doA  {\show\fl\futurelet\fl\doB}
\def\doB#1{\show\fl\futurelet\fl\doC}
\def\doC  {\show\fl}
\process{ ab}
\bye

我们通过命令得到以下输出\show

\doA: \fl=undefined    % clearly, \fl hasn't yet been defined
\doB: \fl=blank space  % \fl has been letted to the blank space
                       % If it were a normal token, we could consume it with an argument,
                       % and would have #1=space in \doB and \fl=a in \doC
\doC: \fl=the letter b % However, the space is skipped when parsing the argument,
                       % so we end up with #1=a in \doB and \fl=b in \doC

\fl=the letter a那么在能够检测和处理空间之后,我们如何进入这样的状态呢?

相关问题:定义\myspace使其与 相等\fl(当 时\fl=blank space)的最佳方式是什么?通过\ifx(或任何其他if)?以下类型的工作(除了虚假空间)。

\def\defmyspace#1{\futurelet\myspace\relax#1}
\defmyspace{ }
\show\myspace          % \myspace=blank space

答案1

您可以\process{ ab}使用\futurelet以下宏:

\def\process#1{\doA#1\end}
\def\doA{\futurelet\fl\doB}
\def\doB{\ifx\fl\end\else \reportfl \afterassignment\doA \fi \let\next= } 
\def\reportfl{\message{\meaning\fl}}

\process{ ab}

\end

我修正了我的代码中的一个错误(Ulrich 和 Donald 的评论)并将报告移到\fl宏中,以便像 这样的标记\if\fi可以被处理。

答案2

受到 wipet 的回答的启发,我建议使用\let-\afterassignment循环:

\def\process#1{\afterassignment\doA\let\next= #1\process\process}
\def\doA{%
  \ifx\next\process\else
    \message{\meaning\next}%
    \afterassignment\doA
  \fi
  \let\next= %
}
    
\process{ ab}

\message{done}

\end

(假设 的参数\process不包含其含义等于 的含义的标记\process。)

控制台输出为:

This is pdfTeX, Version 3.14159265-2.6-1.40.21 (TeX Live 2020) (preloaded format=pdftex)
 restricted \write18 enabled.
entering extended mode
(./test.tex blank space   the letter a the letter b done )
No pages of output.
Transcript written on test.log.

对于您的“相关问题”:

代替

\def\defmyspace#1{\futurelet\myspace\relax#1}
\defmyspace{ }
\show\myspace 

考虑到它只\let ... = ...消耗后面一个可选空间=,并执行以下操作:

\def\myspace#1{#1}
\myspace{\let\myspace= } %
\show\myspace

只要\lccode空格字符的和字符\lccode=不表明\lowercase影响这些字符,您也可以执行以下操作:

\lowercase{\let\myspace= } %
\show\myspace

在 LaTeX 2ε 中\@sptoken可用。



由于 Wipet 已经提供了一个展示用法的出色答案\let\futurelet因此\afterassignment我决定提供一种完全不同的方法来解决这个问题 — — 如果你真的希望从论证的角度来“看待”事物,这种方法可能很适合。

根据您打算执行的具体操作,您可能可以在没有赋值/没有\futurelet/ 的情况下进行尾部递归迭代\let

%%-----------------------------------------------------------------------------
%% Paraphernalia:
%%.............................................................................
\long\def\firstoftwo#1#2{#1}%
\long\def\secondoftwo#1#2{#2}%
\long\def\exchange#1#2{#2#1}%
\chardef\stopromannumeral=`\^^00%
\secondoftwo{}{\long\def\removespace} {}%
%%-----------------------------------------------------------------------------
%% 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>}%
%%
%% 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\CheckWhetherNull#1{%
  \romannumeral\expandafter\secondoftwo\string{\expandafter
  \secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \secondoftwo\string}\expandafter\firstoftwo\expandafter{\expandafter
  \secondoftwo\string}\expandafter\stopromannumeral\secondoftwo}%
  {\expandafter\stopromannumeral\firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is an explicit 
%% catcode-1-character-token
%%.............................................................................
%% \CheckWhetherBrace{<Argument which is to be checked>}%
%%                   {<Tokens to be delivered in case that argument
%%                     which is to be checked has leading
%%                     explicit catcode-1-character-token>}%
%%                   {<Tokens to be delivered in case that argument
%%                     which is to be checked does not have a leading
%%                     explicit catcode-1-character-token>}%
\long\def\CheckWhetherBrace#1{%
  \romannumeral\expandafter\secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\firstoftwo\expandafter{\expandafter
  \secondoftwo\string}\expandafter\stopromannumeral\firstoftwo}%
  {\expandafter\stopromannumeral\secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \CheckWhetherLeadingExplicitSpace{<Argument which is to be checked>}%
%%                                  {<Tokens to be delivered in case
%%                                     <argument which is to be checked>
%%                                    does have a leading explicit space-
%%                                    token>}%
%%                                  {<Tokens to be delivered in case
%%                                     <argument which is to be checked>
%%                                    does not have a leading explicit 
%%                                    space-token>}%
\long\def\CheckWhetherLeadingExplicitSpace#1{%
  \romannumeral\CheckWhetherNull{#1}%
  {\expandafter\stopromannumeral\secondoftwo}%
  {%
    % Let's nest things into \firstoftwo{...}{} to make sure they are nested in braces
    % and thus do not disturb when the test is carried out within \halign/\valign:
    \expandafter\firstoftwo\expandafter{%
      \expandafter\expandafter\expandafter\stopromannumeral
      \romannumeral\expandafter\secondoftwo
      \string{\CheckWhetherLeadingExplicitSpaceB.#1 }{}%
    }{}%
  }%
}%
\long\def\CheckWhetherLeadingExplicitSpaceB#1 {%
  \expandafter\CheckWhetherNull\expandafter{\firstoftwo{}#1}%
  {\exchange{\firstoftwo}}{\exchange{\secondoftwo}}%
  {\expandafter\expandafter\expandafter\stopromannumeral
   \expandafter\expandafter\expandafter}%
  \expandafter\secondoftwo\expandafter{\string}%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%
%%   \romannumeral\ExtractFirstArgLoop{ABCDE\SelDOm} yields  A
%%
%%   \romannumeral\ExtractFirstArgLoop{{AB}CDE\SelDOm} yields  AB
%%
%%   \romannumeral\ExtractFirstArgLoop{\SelDOm ABCDE\SelDOm} yields  \SelDOm
%%.............................................................................
\long\def\RemoveTillSelDOm#1#2\SelDOm{{#1}}%
\long\def\ExtractFirstArgLoop#1{%
  \expandafter\CheckWhetherNull\expandafter{\firstoftwo{}#1}%
  {\expandafter\stopromannumeral\secondoftwo{}#1}%
  {\expandafter\ExtractFirstArgLoop\expandafter{\RemoveTillSelDOm#1}}%
}%
%%-----------------------------------------------------------------------------
%%
\long\def\process#1{%
  \CheckWhetherNull{#1}{}{%
    \CheckWhetherLeadingExplicitSpace{#1}{%
      The first token of {\tt\string\process} is an explicit space token.\hfill\break
      \expandafter\process\expandafter{\removespace#1}%
    }{%
       \CheckWhetherBrace{#1}{%
          The first token of {\tt\string\process} is an opening brace.\hfill\break
          \expandafter\process\expandafter{\romannumeral\ExtractFirstArgLoop{#1\SelDOm}}%
          The first token of {\tt\string\process} is a closing brace.\hfill\break
       }{%
          The first token of {\tt\string\process} is: {\tt\expandafter\string\romannumeral\ExtractFirstArgLoop{#1\SelDOm}}\hfill\break
       }%
       \expandafter\process\expandafter{\firstoftwo{}#1}%
    }%
  }%
}%

\noindent{\tt\string\noindent}\hfill\break
{\tt\string\process\string{ ab\string{cd\string}e f \string\TeX\string}}

\bigskip

\noindent
\process{ ab{cd}e f \TeX}%

\bye

在此处输入图片描述

从您的评论中我了解到该问题与 LaTeX 2ε 中参数的字符串化有关。

请注意,上述方法并不适用于对参数进行正确的字符串化,因为在没有实际检查右括号的情况下就消耗了匹配的花括号。

我可以提供一个字符串化例程来检查左括号和右括号。(除了/之外,可能还有类似/甚至更糟的 / ...){1}2X1Y212

%% Copyright (C) 2019, 2020 by Ulrich Diez ([email protected])
%%
%% This work may be distributed and/or modified under the
%% conditions of the LaTeX Project Public Licence (LPPL), either
%% version 1.3 of this license or (at your option) any later
%% version. (The latest version of this license is in:
%% http://www.latex-project.org/lppl.txt
%% and version 1.3 or later is part of all distributions of LaTeX
%% version 1999/12/01 or later.)
%% The author of this work is Ulrich Diez.
%% This work has the LPPL maintenance status 'not maintained'.
%% Usage of any/every component of this work is at your own risk.
%% There is no warranty - neither for probably included
%% documentation nor for any other part/component of this work.
%% If something breaks, you usually may keep the pieces.
    
\errorcontextlines=10000
    
\documentclass{article}

\makeatletter    
%%=============================================================================
%% Paraphernalia:
%%    \UD@firstoftwo, \UD@secondoftwo,
%%    \UD@PassFirstToSecond, \UD@Exchange, \UD@removespace
%%    \UD@CheckWhetherNull, \UD@CheckWhetherBrace,
%%    \UD@CheckWhetherLeadingSpace, \UD@ExtractFirstArg
%%=============================================================================
\newcommand\UD@firstoftwo[2]{#1}%
\newcommand\UD@secondoftwo[2]{#2}%
\newcommand\UD@PassFirstToSecond[2]{#2{#1}}%
\newcommand\UD@Exchange[2]{#2#1}%
\@ifdefinable\UD@removespace{\UD@Exchange{ }{\def\UD@removespace}{}}%
\@ifdefinable\UD@stopromannumeral{\chardef\UD@stopromannumeral=`\^^00}%
%%-----------------------------------------------------------------------------
%% Check whether argument is empty:
%%.............................................................................
%% \UD@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>}%
%%
%% The gist of this macro comes from Robert R. Schneck's \ifempty-macro:
%% <https://groups.google.com/forum/#!original/comp.text.tex/kuOEIQIrElc/lUg37FmhA74J>
\newcommand\UD@CheckWhetherNull[1]{%
  \romannumeral\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@secondoftwo}{%
  \expandafter\UD@stopromannumeral\UD@firstoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether argument's first token is a catcode-1-character
%%.............................................................................
%% \UD@CheckWhetherBrace{<Argument which is to be checked>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has a leading
%%                        explicit catcode-1-character-token>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked does not have a
%%                        leading explicit catcode-1-character-token>}%
\newcommand\UD@CheckWhetherBrace[1]{%
  \romannumeral\expandafter\UD@secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\expandafter\UD@stopromannumeral\UD@firstoftwo}{%
  \expandafter\UD@stopromannumeral\UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument starts with a space-token
%%.............................................................................
%% \UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked> does have a
%%                               leading explicit space-token>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked> does not have a
%%                               a leading explicit space-token>}%
\newcommand\UD@CheckWhetherLeadingExplicitSpace[1]{%
  \romannumeral\UD@CheckWhetherNull{#1}%
  {\expandafter\UD@stopromannumeral\UD@secondoftwo}%
  {%
    % Let's nest things into \UD@firstoftwo{...}{} to make sure they are nested in braces
    % and thus do not disturb when the test is carried out within \halign/\valign:
    \expandafter\UD@firstoftwo\expandafter{%
      \expandafter\expandafter\expandafter\UD@stopromannumeral
      \romannumeral\expandafter\UD@secondoftwo
      \string{\UD@CheckWhetherLeadingExplicitSpaceB.#1 }{}%
    }{}%
  }%
}%
\@ifdefinable\UD@CheckWhetherLeadingExplicitSpaceB{%
  \long\def\UD@CheckWhetherLeadingExplicitSpaceB#1 {%
    \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
    {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
    {\expandafter\expandafter\expandafter\UD@stopromannumeral
     \expandafter\expandafter\expandafter}%
     \expandafter\UD@secondoftwo\expandafter{\string}%
  }%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%
%%   \UD@ExtractFirstArg{ABCDE} yields  {A}
%%
%%   \UD@ExtractFirstArg{{AB}CDE} yields  {AB}
%%.............................................................................
\@ifdefinable\UD@RemoveTillUD@SelDOm{%
  \long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
}%
\newcommand\UD@ExtractFirstArg[1]{%
  \romannumeral\UD@ExtractFirstArgLoop{#1\UD@SelDOm}%
}%
\newcommand\UD@ExtractFirstArgLoop[1]{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  {\UD@stopromannumeral#1}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%-----------------------------------------------------------------------------    
%% In case an argument's first token is an opening brace, stringify that and
%% add another opening brace before that and remove everything behind the 
%% matching closing brace:
%% \UD@StringifyOpeningBrace{{Foo}bar} yields {{Foo}  whereby the second
%% opening brace is stringified:
%%.............................................................................
\newcommand\UD@StringifyOpeningBrace[1]{%
  \romannumeral
  \expandafter\UD@ExtractFirstArgLoop\expandafter{%
    \romannumeral\expandafter\expandafter\expandafter\UD@stopromannumeral
    \expandafter\expandafter
    \expandafter            {%
    \expandafter\UD@firstoftwo
    \expandafter{%
    \expandafter}%
    \romannumeral\expandafter\expandafter\expandafter\UD@stopromannumeral
    \expandafter\string
    \expandafter}%
    \string#1%
  \UD@SelDOm}%
}%
%%-----------------------------------------------------------------------------    
%% In case an argument's first token is an opening brace, remove everything till 
%% finding the corresponding closing brace. Then stringify that closing brace:
%% \UD@StringifyClosingBrace{{Foo}bar} yields: {}bar} whereby the first closing
%% brace is stringified:
%%.............................................................................
\newcommand\UD@StringifyClosingBrace[1]{%
   \romannumeral\expandafter\expandafter\expandafter
                \UD@StringifyClosingBraceloop
                \UD@ExtractFirstArg{#1}{#1}%
}%
\newcommand\UD@CheckWhetherStringifiedOpenBraceIsSpace[1]{%
%% This can happen when character 32 (space) has catcode 1...
  \expandafter\UD@CheckWhetherLeadingExplicitSpace\expandafter{%
    \romannumeral\expandafter\expandafter\expandafter\UD@stopromannumeral
    \expandafter\UD@secondoftwo
    \expandafter{%
    \expandafter}%
    \expandafter{%
    \romannumeral\expandafter\expandafter\expandafter\UD@stopromannumeral
    \expandafter\UD@firstoftwo
    \expandafter{%
    \expandafter}%
    \romannumeral\expandafter\expandafter\expandafter\UD@stopromannumeral
    \expandafter\string
    \expandafter}%
    \string#1%
  }%
}%
\newcommand\UD@TerminateStringifyClosingBraceloop[2]{%
  \expandafter\expandafter\expandafter\UD@stopromannumeral%
  \expandafter\expandafter
  \expandafter{%
  \expandafter\string      
  \romannumeral\expandafter\expandafter\expandafter\UD@stopromannumeral
  \expandafter#1%
  \string#2%
  }%
}%
\newcommand\UD@StringifyClosingBraceloopRemoveElement[4]{%
  \expandafter\UD@PassFirstToSecond\expandafter{\expandafter
  {\romannumeral\expandafter\UD@secondoftwo\string}{}%
    \UD@CheckWhetherStringifiedOpenBraceIsSpace{#4}{%
      \UD@Exchange{\UD@removespace}%
    }{%
      \UD@Exchange{\UD@firstoftwo\expandafter{\expandafter}}%
    }{%
      \expandafter\expandafter\expandafter\UD@stopromannumeral
      \expandafter#1%
      \romannumeral\expandafter\expandafter\expandafter\UD@stopromannumeral
      \expandafter
    }%
    \string#4%
  }{\expandafter\UD@StringifyClosingBraceloop\expandafter{#2#3}}%
}%
\newcommand\UD@StringifyClosingBraceloop[2]{%
  \UD@CheckWhetherNull{#1}{%
    \UD@CheckWhetherStringifiedOpenBraceIsSpace{#2}{%
      \UD@TerminateStringifyClosingBraceloop{\UD@removespace}%
    }{%
      \UD@TerminateStringifyClosingBraceloop{\UD@firstoftwo\expandafter{\expandafter}}%
    }%
    {#2}%
  }{%
    \UD@CheckWhetherLeadingExplicitSpace{#1}{%
      \UD@StringifyClosingBraceloopRemoveElement
      {\UD@removespace}{\UD@removespace}%
    }{%
      \UD@StringifyClosingBraceloopRemoveElement
      {\UD@firstoftwo\expandafter{\expandafter}}{\UD@firstoftwo{}}%
    }%
    {#1}{#2}%
  }%
}%
%%-----------------------------------------------------------------------------    
%% Apply <action> to the stringification of each token of the argument:
%%
%% \StringifyNAct{<action>}{<token 1><token 2>...<token n>}
%%
%% yields:  <action>{<stringification of token 1>}%
%%          <action>{<stringification of token 2>}%
%%          ...
%%          <action>{<stringification of token n>}%
%%
%% whereby "stringification of token" means the result of applying \string
%% to the token in question.
%% Due to \romannumeral-expansion the result is delivered after two
%% \expandafter-chains.
%% If you leave <action> empty, you can apply a loop on the list formed by
%%   {<stringification of token 1>}%
%%   {<stringification of token 2>}%
%%   ...
%%   {<stringification of token n>}%
%%.............................................................................
\newcommand\StringifyNAct{%
  \romannumeral\StringifyNActLoop{}%
}%
%%.............................................................................
%% \StringifyNActLoop{{<stringification of token 1>}...{<stringification of token k-1>}}%
%%                   {<action>}%
%%                   {<token k>...<token n>}
%%.............................................................................
\newcommand\StringifyNActLoop[3]{%
  \UD@CheckWhetherNull{#3}{%
    \UD@stopromannumeral#1%
  }{%
    \UD@CheckWhetherBrace{#3}{%
      \expandafter\expandafter\expandafter\UD@Exchange
      \expandafter\expandafter\expandafter{%
        \UD@StringifyClosingBrace{#3}%
      }{%
        \expandafter\StringifyNActLoop\expandafter{%
          \romannumeral
          \expandafter\expandafter\expandafter\UD@Exchange
          \expandafter\expandafter\expandafter{\UD@StringifyOpeningBrace{#3}}{\StringifyNActLoop{#1}{#2}}%
        }{#2}%
      }%
    }{%
      \UD@CheckWhetherLeadingExplicitSpace{#3}{%
        \expandafter\UD@PassFirstToSecond\expandafter{\UD@removespace#3}{%
          \StringifyNActLoop{#1#2{ }}{#2}%
        }%
      }{%
        \expandafter\UD@PassFirstToSecond\expandafter{\UD@firstoftwo{}#3}{%
          \expandafter\StringifyNActLoop\expandafter{%
             \romannumeral%
             \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\UD@PassFirstToSecond
             \expandafter\expandafter\expandafter\expandafter\expandafter\expandafter\expandafter{%
               \expandafter\expandafter\expandafter\string
               \expandafter\UD@Exchange
               \romannumeral\UD@ExtractFirstArgLoop{#3\UD@SelDOm}{}%
             }{\UD@stopromannumeral#1#2}%
          }%
          {#2}%
        }%
      }%
    }%
  }%
}%
%%.............................................................................
%% Now a routine which you can apply as <action> within \StringifyNAct:
%%.............................................................................
\newcommand\printstringifiedtoken[1]{%
  A token was stringified as
  \UD@CheckWhetherLeadingExplicitSpace{#1}{%
    \fbox{\texttt{\char`\ \strut}} (explicit space token\strut)%
  }{%
    \fbox{\texttt{#1\strut}}%
  } 
 .\\
}%
%%.............................................................................
%% Now a routine which you can apply when prefering iterating on the result
%% of \StringifyNAct
%%.............................................................................
\newcommand\printstringifiedtokenloop[1]{%
  \ifx\relax#1\expandafter\@gobble\else\expandafter\@firstofone\fi
  {\printstringifiedtoken{#1}\printstringifiedtokenloop}%
}%
\makeatother

\pagestyle{empty}

\begin{document}

\vspace*{-4cm}\enlargethispage{4cm}

\begin{verbatim*}
\noindent
\StringifyNAct{\printstringifiedtoken}{%
  \textbf{\csname @firstofone\endcsname{\LaTeX} is funny.}
}
\end{verbatim*}

yields:\bigskip

\noindent
\StringifyNAct{\printstringifiedtoken}{%
  \textbf{\csname @firstofone\endcsname{\LaTeX} is funny.}
}

(The last explicit space token is due to the \verb|\endlinechar|-thingie 
while the state of \LaTeX's reading-apparatus is in state M (middle of line) 
after a curly closing brace. It also is in that state after an opening
curly brace.)

\newpage
\vspace*{-4cm}\enlargethispage{4cm}

\begin{verbatim*}
\noindent
\expandafter\expandafter
\expandafter\printstringifiedtokenloop
\StringifyNAct{}{%
  \textbf{\csname @firstofone\endcsname{\LaTeX} is funny.}
}%
\relax
\end{verbatim*}

yields:\bigskip

\noindent
\expandafter\expandafter
\expandafter\printstringifiedtokenloop
\StringifyNAct{}{%
  \textbf{\csname @firstofone\endcsname{\LaTeX} is funny.}
}%
\relax

(The last explicit space token is due to the \verb|\endlinechar|-thingie 
while the state of \LaTeX's reading-apparatus is in state M (middle of line) 
after a curly closing brace. It also is in that state after an opening
curly brace.)
\end{document}

在此处输入图片描述

在此处输入图片描述

答案3

tokcycle包设置为循环使用标记。这是最简单的演示,显示您定义了对每个标记要执行的操作...在这里,我将字符括起来,并将空格括起来。

更复杂的指令可以测试标记并根据其 charcode 或 catcode 进行分支。

空间有它们自己的指令,因为 TeX 吸收它们的方式不同,正如您在问题中提到的那样。

我应该指出,使用 tokcycle 的正常方式是,将处理过的标记添加到 toks 寄存器中,然后您可以在最后重新使用它。在这里,由于演示的简单性(例如,标记不会作为参数相互交互),我放弃了它,\cytoks只是即时处理了标记。

最后,我要指出的是,MWE 使用\tokcyclexpress{...}带参数的宏形式。该包还提供了伪环境形式\tokencyclexpress...\endtokencyclexpress

\documentclass {article}
\usepackage{tokcycle}
\begin{document}
\Characterdirective{[#1]}
\Spacedirective{(#1)}
\tokcyclexpress{ ab}
\end{document}

在此处输入图片描述

下面是一个稍微复杂一点的字符指令的例子,其中的a标记还被设为斜体:

\Characterdirective{[\tctestifx{a#1}{\textit{#1}}{#1}]}

在此处输入图片描述


补充

现在,为了好玩,我演示了 tokcycle 如何跟踪标记的主动和隐式性质。请注意,在示例 3 中,a 不是斜体,因为它是由 处理的,它\Macrodirective是通过\def而不是定义的\let。宏指令设置为在括号之间提供标记。

\documentclass {article}
\usepackage{tokcycle}
\begin{document}
\newcommand\checkAI{$\ifimplicittok_\mathrm{implicit}\fi
   \ifactivetok^\mathrm{active}\fi$}
\Characterdirective{[\tctestifx{a#1}{\textit{#1}}{#1}]\checkAI}
\Macrodirective{\{#1\}\checkAI}
\Spacedirective{(#1)\checkAI}

1. \tokcyclexpress{ ab}

2.\let\q a
\tokcyclexpress{ \q b}

3.\catcode`Q=\active
\defQ{a}
\tokcyclexpress{ Qb}

4.\letQa
\tokcyclexpress{ Qb}

5. \def\:{\let\z= }\:
\tokcyclexpress{\z Qb}

6. \catcode`Z=\active
\def\:{\let Z= }\:
\tokcyclexpress{ZQb}

7. \tokcyclexpress{\space Qb}
\end{document}

在此处输入图片描述

相关内容