环境参数中的宏扩展

环境参数中的宏扩展

我想生成一个根据给定的参数略有变化的环境。原因是文档中存在不同的语言环境。我定义了一个命令,EmitLanguageStr它根据传递的参数确定参数otherlanguage。例如,\EmitLanguageStr[en] -> english有关更多上下文,请参阅以下问题:[12] 这是简化后的环境。

\renewenvironment{abstract}[1][en]{%
    \section*{\EmitAbstractStr[#1]}
    \begin{otherlanguage}{\EmitLanguageStr[#1]}
        \@author\par
        \textbf{\EmitTitle[#1]}\par
}{%
    \end{otherlanguage}
}

事实证明其他人也遇到了同样的问题(),但这个解决方案对我来说不太管用。答案表明,用以下两种方式之一包装环境将允许扩展参数:

% Create a macro
\newcommand\testlang{english}

% Expand the argument once:
\newenvironment{Otherlanguage}[1]{%
    \expandafter\otherlanguage\expandafter{#1}%
}{\endotherlanguage}

% Alternative: argument is fully expanded
\newenvironment{Otherlanguage}[1]{%
    \begingroup
    \edef\temp{\endgroup\noexpand\otherlanguage{#1}}%
    \temp
}{\endotherlanguage}

% usage    
\begin{Otherlanguage}{\testlang}
    ...
\end{Otherlangauge}

首先,如果没有这些包装器,babel 会给出错误消息,提示语言EmitLanguageStr[en]未定义。链接问题中已经解决了这个问题。但是,给出的解决方案会产生另一种错误:Use of \EmitLanguageStr doesn't match its definition

如何正确扩展命令?


以下是该情况的完整示例。此方法可行,但将参数切换Otherlanguage为注释中的参数时会引发所述错误。

\documentclass[a4paper,12pt]{article}

\usepackage{ifthen}
\usepackage{pgffor}

\makeatletter

\newcommand\SetDefaultValue[2]{%
    \global\@namedef{#2:#1}{%
        {\scriptsize (Use {\tt\textbackslash #2[#1]} to replace this text.)}%
    }%
}

\newcommand\MakeLocaleVar[2][en,fi]{%
    \foreach \n in {#1}{%
        \expandafter\SetDefaultValue\expandafter{\n}{#2}
    }%
    \expandafter\newcommand\csname #2\endcsname[2][]{%
        \ifthenelse{\equal{##1}{}}{%
            \foreach \n in {#1}{%
                \global\@namedef{#2:\n}{##2}%
            }%
        }{%
            \global\@namedef{#2:##1}{##2}%
        }%
    }%
    \expandafter\newcommand\csname Emit#2\endcsname[1][en]{\@nameuse{#2:##1}}%
}

% Variables set in document
\MakeLocaleVar{Title}
\MakeLocaleVar{Year}

%\newenvironment{Otherlanguage}[1]{%
%   \expandafter\otherlanguage\expandafter{#1}%
%}{\endotherlanguage}

\newenvironment{Otherlanguage}[1]{%
    \begingroup
    \edef\temp{\endgroup\noexpand\otherlanguage{#1}}%
    \temp
}{\endotherlanguage}

\MakeLocaleVar{AbstractStr}
\AbstractStr[en]{ABSTRACT}
\AbstractStr[fi]{TIIVISTELMÄ}

\MakeLocaleVar{LanguageStr}
\LanguageStr[en]{english}
\LanguageStr[fi]{finnish}

\renewenvironment{abstract}[1][en]{%
    \section*{\EmitAbstractStr[#1]}
    \begin{Otherlanguage}{english}%\EmitLanguageStr[#1]
        \@author\par
        \textbf{\EmitTitle[#1]}\par
        \EmitYear\par
        \par
    }{%
    \end{Otherlanguage}
}

\makeatother

\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[finnish, english]{babel}

\author{Handsome Devil}
\Year{2019}

\Title[en]{Title in English}
\Title[fi]{Otsikko suomeksi}

\begin{document}
\selectlanguage{english}

\begin{abstract}
    Abstract environment in English.
\end{abstract}

\begin{abstract}[fi]
    Abstract environment in Finnish.
\end{abstract}
\end{document}

答案1

\MakeLocaleVar{LanguageStr}原因\EmitLanguageStr定义为:

\newcommand\EmitLanguageStr[1][en]{\@nameuse{LanguageStr:#1}}%

\EmitLanguageStr处理可选参数。

如果你看一下\EmitLanguageStrvia \show\EmitLanguageStr,你会发现:

> \EmitLanguageStr=macro:
->\@protected@testopt \EmitLanguageStr \\EmitLanguageStr {en}.

看看\@protected@testopt收益率:

> \@protected@testopt=macro:
#1->\ifx \protect \@typeset@protect \expandafter \@testopt \else \@x@protect #1
\fi .

看看\@testopt收益率:

> \@testopt=\long macro:
#1#2->\kernel@ifnextchar [{#1}{#1[{#2}]}.

看看\kernel@ifnextchar收益率:

> \kernel@ifnextchar=\long macro:
#1#2#3->\let \reserved@d =#1\def \reserved@a {#2}\def \reserved@b {#3}\futurele
t \@let@token \@ifnch .

看看\@ifnch收益率:

> \@ifnch=macro:
->\ifx \@let@token \@sptoken \let \reserved@c \@xifnch \else \ifx \@let@token \
reserved@d \let \reserved@c \reserved@a \else \let \reserved@c \reserved@b \fi 
\fi \reserved@c .

看看\@xifnch收益率:

> \@xifnch=macro:
 ->\futurelet \@let@token \@ifnch .

正如您所看到的,在通过检查可选参数是否存在时,需要对不可扩展的\let- 和- 赋值进行大量的操作。\futurelet\kernel@ifnextchar [

因此,\EmitLanguageStr它不仅传递构成语言名称的那些字符标记。它还产生许多不可扩展的赋值标记,如\let和,\futurelet用于检查可选参数是否存在。
这些标记不会在纯扩展上下文中消失,而获取语言名称并将其作为参数传递给环境将是纯扩展上下文。扩展后,这些不可扩展的赋值标记仍保留在标记流中,因为扩展发生在 (La)TeX 的“食道”中,而赋值的执行发生在 (La)TeX 的“胃”中。

简而言之:

只要\EmitLanguageStr处理可选参数,其中检测可选参数存在的技术本身涉及在扩展期间不会消失的不可扩展赋值标记,您就不能\EmitLanguageStr在纯扩展上下文中使用它来仅获取构成语言名称的那些标记。

但是,例如,您可以创建一个\EmitLanguageStr带有附加参数的变体,该参数包含要将发出的字符串作为参数传递给的标记。我称该宏为\PassLanguageStrTo

\documentclass[a4paper,12pt]{article}

\usepackage{ifthen}
\usepackage{pgffor}

\makeatletter

\newcommand\SetDefaultValue[2]{%
    \global\@namedef{#2:#1}{%
        {\scriptsize (Use {\tt\textbackslash #2[#1]} to replace this text.)}%
    }%
}

\newcommand\PassFirstToSecond[2]{#2{#1}}%

\newcommand\MakeLocaleVar[2][en,fi]{%
    \foreach \n in {#1}{%
        \expandafter\SetDefaultValue\expandafter{\n}{#2}%%%%
    }%
    \expandafter\newcommand\csname #2\endcsname[2][]{%
        \ifthenelse{\equal{##1}{}}{%
            \foreach \n in {#1}{%
                \global\@namedef{#2:\n}{##2}%
            }%
        }{%
            \global\@namedef{#2:##1}{##2}%
        }%
    }%
    \expandafter\newcommand\csname Emit#2\endcsname[1][en]{\@nameuse{#2:##1}}%
    \expandafter\newcommand\csname Pass#2To\endcsname[2][en]{%
      \expandafter\expandafter\expandafter\PassFirstToSecond
      \expandafter\expandafter\expandafter{\csname#2:##1\endcsname}{##2}%
    }%
}

% Variables set in document
\MakeLocaleVar{Title}
\MakeLocaleVar{Year}


\MakeLocaleVar{AbstractStr}
\AbstractStr[en]{ABSTRACT}
\AbstractStr[fi]{TIIVISTELMÄ}

\MakeLocaleVar{LanguageStr}
\LanguageStr[en]{english}
\LanguageStr[fi]{finnish}

\renewenvironment{abstract}[1][en]{%
    \section*{\EmitAbstractStr[{#1}]}%%%%
    \PassLanguageStrTo[{#1}]{\begin{otherlanguage}}%
        \@author\par
        \textbf{\EmitTitle[{#1}]}\par
        \EmitYear\par
        \par
    }{%
    \end{otherlanguage}%%%%
}

\makeatother

\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[finnish, english]{babel}

\author{Handsome Devil}
\Year{2019}

\Title[en]{Title in English}
\Title[fi]{Otsikko suomeksi}

\begin{document}
\selectlanguage{english}

\begin{abstract}
    Abstract environment in English.
\end{abstract}

\begin{abstract}[fi]
    Abstract environment in Finnish.
\end{abstract}
\end{document}

相关内容