환경 인수에서 매크로 확장

환경 인수에서 매크로 확장

주어진 인수에 따라 조금씩 변화하는 환경을 생성하고 싶습니다. 그 이유는 문서의 로케일이 다르기 때문입니다. 전달된 인수에 따라 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선택적 인수를 처리합니다.

\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와 같은 확장할 수 없는 할당 토큰을 많이 생성합니다 . 이러한 토큰은 순수 확장 컨텍스트 내에서 사라지지 않지만 환경에 인수로 전달하기 위한 언어 이름을 얻는 것은 순수 확장 컨텍스트입니다. 확장 후에 이러한 확장할 수 없는 할당 토큰은 토큰 스트림에 남아 있습니다. 왜냐하면 확장이 (La)TeX의 "식도"에서 일어나는 반면 할당 수행은 (La)TeX의 "위"에서 일어나기 때문입니다.\futurelet

간단히 말해서:

\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}

관련 정보