如何将可选参数传递给具有逐字内容的环境?

如何将可选参数传递给具有逐字内容的环境?

这是为了澄清关于 xport 的这个问题(现已删除,抱歉)。现已删除的问题包含试图让我的答案关于 xport 的这个问题为。。。工作选修的参数,但代码失败。简短的例子:如果删除 ,则以下代码不会编译[optional argument]

\documentclass{minimal}
\usepackage{listings,fancyvrb}

\newenvironment{Row}[1][]
    {\VerbatimEnvironment
     \begin{VerbatimOut}{\jobname.tmp}}
    {\end{VerbatimOut}\lstinputlisting{\jobname.tmp}}

\begin{document}
\begin{Row}[optional argument]
\relax
\end{Row}
\end{document}

错误信息说

! FancyVerb Error:
  Extraneous input `\relax ' between \begin{Row}[<key=value>] and line end

当然,可选参数(事实上整个构造)在这里毫无意义,但嘿,这是一个最小的例子。我认为,让 xport 问题中的示例也适用于可选参数是相当有趣的。

问题是:这里到底发生了什么,以及如何修复代码?

(到目前为止我发现:\begin{VerbatimOut}{\jobname.tmp}}想要在它之后立即看到一个换行符(尝试放置一个),并且这会因寻找可选参数%而变得混乱。)\begin{Row}

答案1

编辑:TH. 的回答简单多了。下面的代码应该更少地依赖于 的实现细节\VerbatimEnvironment,但这是它唯一的优点。

我建议使用以下代码。这个想法是创建一个新环境(我将其称为Row,将旧环境重命名RowoldRow)“手动”检查可选参数,并将相关内容写入文件。然后\jobname.row读取文件(),以便fancyvrb可以顺利设置相关的 catcode。

所有这些可能都可以使用来完成\scantokens,但我仍然对里面的换行符感到很困惑\scantokens

我还添加了*#1*您对环境的定义:这样,我们就可以检查可选参数发生的情况。

\documentclass{minimal}
\usepackage{listings,fancyvrb}

\newenvironment{oldRow}[1][]
{*#1*\VerbatimEnvironment
  \begin{VerbatimOut}{\jobname.tmp}}
  {\end{VerbatimOut}\lstinputlisting{\jobname.tmp}}


\begin{document}

% ==============================================
\makeatletter
\newwrite\RowWrite

\newcommand{\makeallother}{%
  \count0=0\relax 
  \loop\relax 
  \catcode\count0=12\relax 
  \advance\count0 by 1\relax 
  \ifnum\count0<256\relax
  \repeat\relax}

\newenvironment{Row}{%
  \makeallother
  \futurelet\next
  \Row@aux@i}{}

\newcommand{\Row@aux@i}{%
  \def\Row@optional@arg{}%
  \ifx[\next
  \expandafter\Row@grab@until@bracket
  \else
  \expandafter\Row@grab@until@end
  \fi}

\def\Row@grab@until@bracket[#1]{%
  \def\Row@optional@arg{#1}\Row@grab@until@end}

% First expand the \csname...\endcsname,
% then the \string, then the \def.
\expandafter\expandafter\expandafter\def
\expandafter\expandafter\expandafter\Row@grab@until@end
\expandafter\expandafter\expandafter#%
\expandafter\expandafter\expandafter1%
\expandafter\string\csname end{Row}\endcsname{%
  \begingroup%
  \newlinechar=`\^^M%
  \immediate\openout\RowWrite\jobname.row\relax%
  \immediate\write\RowWrite{%
    \string\begin{oldRow}[\Row@optional@arg]#1\string\end{oldRow}%
  }%
  \immediate\closeout\RowWrite%
  \endgroup%
  \end{Row}\input{\jobname.row}%
}

\makeatother
% ==============================================

\begin{Row}
  \relax
  \relax
\end{Row}

\begin{Row}[option]
  \relax
  \relax
\end{Row}


\end{document}

答案2

我可以回答发生了什么,但我无法为您提供解决方案。

实际情况是,在查找可选参数时,^^MTeX 在行尾插入的 被标记化。由于 TeX 处于状态 M,且^^M类别代码为 5,因此它被转换为空格标记。LaTeX会吞噬空格并继续寻找非空格字符。它找到了非\kernel@ifnextchar空格字符,因此将其扩展为。\relax[\\Row[{}]

\\Row[#1]具有开始环境代码。\VerbatimOut将类别代码更改^^M为 active,然后寻找^^M要跟随的 active。但是,该类别代码已使用类别代码 5 进行标记,然后被吞掉。由于它找到了\relax,因此它会发出抱怨。

编辑:
如果您像我一样,通过追踪这条线很难看出空间是如何被吞噬的,那么关键就是这条线。

\reserved@c  ->\futurelet \@let@token \@ifnch
            ^

我用插入符号标记的额外空格是定义的一部分\@xifnch

\def\:{\@xifnch} \expandafter\def\: {\futurelet\@let@token\@ifnch}

编辑23
抱歉,我应该多考虑一下。这并不难修复。我原来的评论给出了如何修复它的提示,只需将其设置为^^M活动字符!更好的解决方案是^^M在没有可选参数的情况下重新插入一个活动字符。这样,可选参数就可以放在下一行,就像\begin{Row}下面的第三次使用一样。

\documentclass{minimal}
\usepackage{listings,fancyvrb}

\begingroup
\catcode`\^^M\active%
\global\def\activeeol{^^M}%
\endgroup
\makeatletter
\newenvironment{Row}{%
        \@ifnextchar[\Row@\Row@noargs
}{%
        \end{VerbatimOut}%
        \lstinputlisting{\jobname.tmp}%
}
\def\Row@[#1]{%
        #1\par
        \VerbatimEnvironment
        \begin{VerbatimOut}{\jobname.tmp}%
}
\def\Row@noargs#1{%
        \edef\temp{[]\activeeol\string#1}%
        \expandafter\Row@\temp
}
\makeatother
\begin{document}
\begin{Row}[optional argument]
\relax
\end{Row}

\begin{Row}
\relax
\end{Row}

\begin{Row}
[Another optional argument]
\relax
\end{Row}
\end{document}

答案3

正如我从 TH 对他的回答的评论中了解到的那样,真正的罪魁祸首是\futurelet用于检测是否有可选参数的 TeX 原语。如果没有,则行尾字符^^M(通常具有 catcode 5)将被 标记化\futurelet,这意味着 catcode 不能再更改了。现在\begin{VerbatimOut}将 catcode 更改^^M为 13(活动),但这对已经标记化的 没有任何影响^^M,并且会出现错误。

fancyvrb这是另一个与的定义类似的解决方案\FV@Environment。与 TH 的解决方案不同,它不是允许可选参数从下一行开始(但它允许在 之前有空格[)。但是,这也许是正确的行为:谁知道呢;代码可能以 开头[。因此,当您编译下面的代码时,[Another optional argument]将是第三段代码的第一行。

\documentclass{minimal}
\usepackage{listings,fancyvrb}

\makeatletter
\newenvironment{Row}{%
    \catcode`\^^M=\active
    \@ifnextchar[%
        {\catcode`\^^M=5 \Row@}
        {\catcode`\^^M=5 \Row@[]}
}{%
    \end{VerbatimOut}%
    \lstinputlisting{\jobname.tmp}%
}
\def\Row@[#1]{%
    *#1*\par
    \VerbatimEnvironment
    \begin{VerbatimOut}{\jobname.tmp}%
}
\makeatother
\begin{document}
\begin{Row}[optional argument]
\large a
\end{Row}

\begin{Row}
\large a
\end{Row}

\begin{Row}
[Another optional argument]
\large a
\end{Row}
\end{document}

答案4

我在使用自定义逐字环境时遇到了同样的问题。但是,我对 TeX 内部机制不太了解,所以我不想弄乱^^M这里描述的内容。(我不知道如何向我的合著者解释它,所以我不想在我们的论文中使用它。)

相反,我在可选参数后添加了一个必需(但未使用)的参数,这似乎解决了解析问题。即,

% Original (broken) version
\newenvironment{MyEnv}[1][default-value]{...}{...}

% Updated (work-around) version
\newenvironment{MyEnv}[2][default-value]{...}{...}
%                      ^ extra (unused) explicit arg

用法如下:

\begin{MyEnv}{}
% uses default value
\end{MyEnv}

\begin{MyEnv}[X]{}
% uses custom value "X"
\end{MyEnv}

这显然不是最理想的,但我发现这比添加一堆我和我的合著者都不会理解的神秘的 TeX 命令要好……


注意:+1 其他答案实际上解释了发生了什么。如果不是这里的答案很棒,我就不会意识到添加一个额外的参数可能是解决这个问题的简单方法。

相关内容