
我想產生一個根據為其給出的參數稍微改變的環境。原因是文件中有不同的區域設定。我定義了一個命令,它根據傳遞的參數來EmitLanguageStr
決定參數。otherlanguage
例如,\EmitLanguageStr[en] -> english
。請參閱這些問題以了解更多背景資訊:[1,2]。這是被剝奪的環境。
\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
處理可選參數。
如果你查看\EmitLanguageStr
via \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}