将非 \long 宏调用为 \long

将非 \long 宏调用为 \long

假设一个给定的宏尚未被\defined \long,但我想\par在参数中使用 s 来调用它。有没有办法关闭TeX一次调用的错误检查?我一直在做的大致是

\def\short#1{#1}
%\short{a
%
%b}
    %% The commented code above causes an error
\def\aslong{\def\hiddenpar{\par}}
\aslong\short{a\hiddenpar b}

这种方法是可行的,因为它实现了所需的扩展,但很难说是优雅的。我想可以使用一些 cat-code monkeying 来实现稍微优雅一些​​的调用语法,但我能想到的一切都将变得更加脆弱。

答案1

您无法关闭错误检查,但是普通的(和乳胶)具有(而不是)原始的\endgraf,并且正是出于这个原因,因此您可以使用\let\def\par

\short{a\endgraf b}

唯一的其他选择是定义\long命令的一个版本,如果可以复制源代码,这很简单,但如果只有宏可用,则可能会很棘手,尤其是当它具有分隔参数时。如果 etex 可用,基本上您可以使用\scantokens它来重新解析\long\def可以从 的输出构建的\meaning\short

答案2

如果你有一个简单的宏,比如说有两个未限定的参数,那么可以对其进行修补,使其\long不再需要 e-TeX:

\def\foo#1#2{#1-#2}

\begingroup\toks0=\expandafter{\foo{#1}{#2}}
\edef\x{\endgroup\long\def\noexpand\foo##1##2{\the\toks0 }}\x

\foo{a\par b}{c\par d}

{\tt\meaning\foo}

(看https://tex.stackexchange.com/a/39980/4427以解释这个技巧)。

在此处输入图片描述

如果宏有分隔参数或者您不想费心知道它需要多少个参数,那么它\scantokens是您的朋友,但需要启用 e-TeX 的引擎,例如 pdfTeX、XeTeX 或 LuaTeX。

\def\makelong#1{%
  \expandafter\getparts\meaning#1\relax
  \domakelong#1%
}

\begingroup
\catcode`M=12 \catcode`A=12 \catcode`C=12 \catcode`R=12 \catcode`O=12
\lowercase{\endgroup\def\getparts#1MACRO:#2->#3\relax}{%
  \def\prefixes{#1}%
  \def\parameters{#2}%
  \def\replacement{#3}%
}

\def\domakelong#1{%
  \edef\temp{\long\prefixes\def\noexpand#1\parameters{\replacement}}%
  \scantokens\expandafter{\temp}%
}

\protected\def\short#1{==#1==}
\def\nonlong#1+#2-{(#1)--(#2)}

\makelong\short
\makelong\nonlong

\short{a\par b}

\nonlong X\par Y+Z\par W-

{\tt\meaning\short}

{\tt\meaning\nonlong}

\bye

在此处输入图片描述

答案3

如果您使用 LuaTeX,则可以设置\suppresslongerror为正值。例如

\def\short#1{#1}

\suppresslongerror=1
\short{a

b}
\suppresslongerror=0

或者

\long\def\forcelong#1{\suppresslongerror=1 #1\suppresslongerror=0 }

\forcelong{\short{a

b}}

答案4

我只能即兴地提供一个\romannumeral0扩展驱动的递归例程\ReplaceEveryPar

\ReplaceEveryPar{⟨replacement text⟩}%
                {⟨arbitrary token-sequence where to replace \par⟩}%

\par的每个控制字标记⟨arbitrary token-sequence where to replace \par⟩将被替换为⟨replacement text⟩

经过两个扩展步骤后即可获得结果,即在扩展上下文中,\ReplaceEveryPar经过两次“命中”后即可获得结果\expandafter

作为副作用,该例程确实通过匹配 catcode 1 和 2 的花括号对来替换 catcode 1 和 2 的匹配显式字符标记对。

我想在大多数情况下这不会是个问题,因为通常花括号是类别代码 1/2 的唯一字符......

在某些情况下您可以使用该例程来替换每个标记\par,例如,\csname par\endcsname当有一个标记序列形成对短宏的调用时,该序列\par出现在短宏的参数中:

\def\removemacrophrase#1>{}%

\def\MyShortMacro#1#2{%
  \ifvmode\noindent\else\hfil\break\fi
  \def\tempa{#1}%
  Short macro's first argument in parentheses: (\expandafter\removemacrophrase\meaning\tempa)%
  \hfil\break
  \def\tempa{#2}%
  Short macro's second argument in parentheses: (\expandafter\removemacrophrase\meaning\tempa)%
}%

序列

\ReplaceEveryPar{\csname par\endcsname}{%
  \MyShortMacro{A\par B}{C\par D}%
}%

产量

\MyShortMacro{A\csname par\endcsname B}{C\csname par\endcsname D}%

,从而得到

  \ifvmode\noindent\else\hfil\break\fi
  \def\tempa{A\csname par\endcsname B}%
  Short macro's first argument in parentheses: (\expandafter\removemacrophrase\meaning\tempa)%
  \hfil\break
  \def\tempa{C\csname par\endcsname D}%
  Short macro's second argument in parentheses: (\expandafter\removemacrophrase\meaning\tempa)%

上面我写了“在某些情况下”,因为有些情况下事情会变得棘手:

例如,对于参数由控制字标记分隔的(短)宏,\par应该用参数替换该控制字标记,而不是用参数分隔符替换。

如果在使用 -notation 定义宏时,\par让其等于,则情况会变得更糟,该宏的最后一个参数由控制字标记分隔,该控制字标记将重新插入到该宏的替换文本后面的标记流中。{#\par\par

\catcode`\@=11
%%=============================================================================
%% Paraphernalia:
%%    \UD@firstoftwo, \UD@secondoftwo,
%%    \UD@PassFirstToSecond, \UD@Exchange, \UD@removespace
%%    \UD@CheckWhetherNull, \UD@CheckWhetherBrace,
%%    \UD@CheckWhetherLeadingSpace, \UD@ExtractFirstArg
%%=============================================================================
\long\def\UD@firstoftwo#1#2{#1}%
\long\def\UD@secondoftwo#1#2{#2}%
\long\def\UD@PassFirstToSecond#1#2{#2{#1}}%
\long\def\UD@Exchange#1#2{#2#1}%
\UD@firstoftwo{\def\UD@removespace}{} {}%
%%-----------------------------------------------------------------------------
%% 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>
\long\def\UD@CheckWhetherNull#1{%
  \romannumeral0\expandafter\UD@secondoftwo\string{\expandafter
  \UD@secondoftwo\expandafter{\expandafter{\string#1}\expandafter
  \UD@secondoftwo\string}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
  {\UD@firstoftwo\expandafter{} \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 leading
%%                        catcode-1-token>}%
%%                      {<Tokens to be delivered in case that argument
%%                        which is to be checked has no leading
%%                        catcode-1-token>}%
\long\def\UD@CheckWhetherBrace#1{%
  \romannumeral0\expandafter\UD@secondoftwo\expandafter{\expandafter{%
  \string#1.}\expandafter\UD@firstoftwo\expandafter{\expandafter
  \UD@secondoftwo\string}\UD@firstoftwo\expandafter{} \UD@firstoftwo}%
  {\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
}%
%%-----------------------------------------------------------------------------
%% Check whether brace-balanced argument's first token is an explicit
%% space token
%%.............................................................................
%% \UD@CheckWhetherLeadingSpace{<Argument which is to be checked>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked>'s 1st token is a
%%                               space-token>}%
%%                             {<Tokens to be delivered in case <argument
%%                               which is to be checked>'s 1st token is not
%%                               a space-token>}%
\long\def\UD@CheckWhetherLeadingSpace#1{%
  \romannumeral0\UD@CheckWhetherNull{#1}%
  {\UD@firstoftwo\expandafter{} \UD@secondoftwo}%
  {\expandafter\UD@secondoftwo\string{\UD@CheckWhetherLeadingSpaceB.#1 }{}}%
}%
\long\def\UD@CheckWhetherLeadingSpaceB#1 {%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@secondoftwo#1{}}%
  {\UD@Exchange{\UD@firstoftwo}}{\UD@Exchange{\UD@secondoftwo}}%
  {\UD@Exchange{ }{\expandafter\expandafter\expandafter\expandafter
   \expandafter\expandafter\expandafter}\expandafter\expandafter
   \expandafter}\expandafter\UD@secondoftwo\expandafter{\string}%
}%
%%-----------------------------------------------------------------------------
%% Extract first inner undelimited argument:
%%
%%   \UD@ExtractFirstArg{ABCDE} yields  {A}
%%
%%   \UD@ExtractFirstArg{{AB}CDE} yields  {AB}
%%
%% The `\romannumeral0-expansion-driven loop in terms of 
%% \UD@ExtractFirstArgLoop has 
%%  ( <amount of non-brace-nested \UD@Seldom not occurring as
%%    the very first token within \UD@ExtractFirstArg's 
%%    argument> + 2 )
%% iterations.
%% This implies: Usage of \UD@SelDOm within \UD@ExtractFirstArg's 
%% argument is _not_ forbidden. 
%%.............................................................................
\long\def\UD@RemoveTillUD@SelDOm#1#2\UD@SelDOm{{#1}}%
\long\def\UD@ExtractFirstArg#1{%
  \romannumeral0%
  \UD@ExtractFirstArgLoop{#1\UD@SelDOm}%
}%
\long\def\UD@ExtractFirstArgLoop#1{%
  \expandafter\UD@CheckWhetherNull\expandafter{\UD@firstoftwo{}#1}%
  { #1}%
  {\expandafter\UD@ExtractFirstArgLoop\expandafter{\UD@RemoveTillUD@SelDOm#1}}%
}%
%%=============================================================================
%% Check Whether argument which must consist of a single token is the token 
%% \par
%%-----------------------------------------------------------------------------
\long\def\UD@CheckWhetherpar#1#2#3{%
  \UD@@Checkkwhetherparfork#1{#2}\par{#3}\par\par\par
}%
\long\def\UD@@Checkkwhetherparfork#1\par#2#3\par\par\par{#2}%
%%=============================================================================
%% \ReplaceEveryPar{<replacement text>}{<argument where to replace \par>}%
%%
%%   Each control word token \par of the <argument where to replace \par>
%%   will be replaced by <replacement text>.
%%
%%   You obtain the result after two expansion-steps, i.e., 
%%   in expansion-contexts you get the result after "hitting" 
%%   \ReplaceEveryPar by two \expandafter.
%%   
%%   As a side-effect, the routine does replace matching pairs of explicit
%%   character tokens of catcode 1 and 2 by matching pairs of curly braces
%%   of catcode 1 and 2.
%%   I suppose this won't be a problem in most situations as usually the
%%   curly braces are the only characters of category code 1 / 2...
%%-----------------------------------------------------------------------------
\long\def\ReplaceEveryPar#1#2{%
   \romannumeral0\UD@ReplaceEveryParLoop{#2}{}{#1}%
}%
\long\def\UD@ReplaceEveryParLoop#1#2#3{%
  % #1 = remaining arbitrary token-sequence where to replace \par
  % #2 = token-sequence with replacement applied constructed so far
  % #3 = replacement text
  \UD@CheckWhetherNull{#1}{ #2}{%
    \expandafter\UD@ReplaceEveryParLoop
    \romannumeral0%
    \UD@CheckWhetherLeadingSpace{#1}{%
       \expandafter{\UD@removespace#1}{#2 }%
    }{%
      \expandafter\UD@PassFirstToSecond\expandafter{%
        \romannumeral0%
        \UD@CheckWhetherBrace{#1}{%
          \expandafter\UD@PassFirstToSecond
          \expandafter{%
            \romannumeral0%
            \expandafter\UD@ReplaceEveryParLoop
            \romannumeral0%
            \UD@ExtractFirstArgLoop{#1\UD@SelDOm}{}{#3}%
          }{ #2}%
        }{%
          \expandafter\UD@CheckWhetherpar
          \romannumeral0\UD@ExtractFirstArgLoop{#1\UD@SelDOm}{ #2#3}{%
            \expandafter\UD@Exchange
            \romannumeral0\UD@ExtractFirstArgLoop{#1\UD@SelDOm}{ #2}%
          }%
        }%
      }{%
        \expandafter{\UD@firstoftwo{}#1}%
      }%
    }%
    {#3}%
  }%
}%
\catcode`\@=12
%%=============================================================================

\tt\frenchspacing

\ReplaceEveryPar{\Rap}{%
  \def\macro#1{text\par #1 text{\par}}%
}%

\string\macro: \meaning\macro

\bigskip

\def\removemacrophrase#1>{}%

\def\MyShortMacro#1#2{%
  \ifvmode\noindent\else\hfil\break\fi
  \def\tempa{#1}%
  Short macro's first argument in parentheses: (\expandafter\removemacrophrase\meaning\tempa)%
  \hfil\break
  \def\tempa{#2}%
  Short macro's second argument in parentheses: (\expandafter\removemacrophrase\meaning\tempa)%
}%

\ReplaceEveryPar{\csname par\endcsname}{%
  \MyShortMacro{A\par B}{C\par D}%
}%

\bye

在此处输入图片描述

相关内容