루프를 사용하여 2차 매크로 일반화

루프를 사용하여 2차 매크로 일반화

명령을 정의하는 데 사용할 수 있는 매크로를 만들고 싶습니다. 이러한 명령은 변수 또는 변수 구조체처럼 동작하므로 여러 값을 포함할 수 있습니다. "멤버"는 선택적 인수를 통해 전달됩니다. 저는 이를 사용하여 템플릿 환경을 정의하고 문서에 동시에 표시되어야 하는 다양한 언어에 대한 다양한 문자열을 보유합니다.

여기에 내가 가지고 있고 작동하는 것이 있습니다.

\newcommand\MakeLocaleVar[1]{%
    \global\@namedef{#1:en}{{\scriptsize (Use {\tt\textbackslash #1[en]} to replace this text.)}}%
    \global\@namedef{#1:fi}{{\scriptsize (Use {\tt\textbackslash #1[fi]} to replace this text.)}}%
    \expandafter\newcommand\csname #1\endcsname[2][]{%
        \ifthenelse{\equal{##1}{}}{%
            \global\@namedef{#1:en}{##2}%
            \global\@namedef{#1:fi}{##2}%
        }{%
            \global\@namedef{#1:##1}{##2}%
        }%
    }%
    \expandafter\newcommand\csname Emit#1\endcsname[1][en]{\@nameuse{#1:##1}}%
}

먼저 기본값이 설정됩니다. 그런 다음 선택적 인수에 따라 값을 설정하는 명령이 생성됩니다. 없는 경우 모든 로캘에 대한 값을 설정합니다.

% In cls: define command
\MakeLocaleVar{Faculty}

% In main tex: set values
\Faculty{This faculty} % for all values
\Faculty[fi]{Tämä tiedekunta} % for a specific one

% In cls environments: use values
\EmitFaculty[en]
\EmitFaculty[fi]

% Now in addition I'd like to be able to:
\MakeLocaleVar[en,fi,de]{Faculty}

임의의 로케일을 허용하도록 명령을 수정하려고 했지만 뭔가 작동하지 않습니다.

\newcommand\MakeLocaleVar[2][en,fi]{%
    \foreach \n in {#1}{%
        \global\@namedef{#2:\n}{%
            {\scriptsize (Use {\tt\textbackslash #2[\n]} to replace this text.)}%
        }%
    }%
    \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}}%
}

사용되는 값을 설정하면 모든 것이 괜찮고 멋집니다. 내 사용자 정의 환경이 중단되고 표시되는 기본 메시지가 Use \Cmd[] to...로케일 이름이 없는 값이 설정되지 않은 경우입니다 .

무슨 일이 일어나고 있는지 아시나요?

답변1

의견에서 언급했듯이 주요 문제는 \n대체 텍스트의 \@namedef이 값으로 확장되지 않았다는 것입니다. Faculty:fi예를 들어 대체 텍스트는 문자 그대로 유지되었습니다.

{\scriptsize (Use {\tt\textbackslash #2[\n]} to replace this text.)}%

와 함께 \n. 매크로가 호출되는 대부분의 상황에서는 \n정의되지 않고 오류가 발생합니다. 매크로 대체 텍스트를 구성하여 \n확장(예: fi또는 )이 되기를 원합니다 en. 이를 올바르게 수행하는 가장 쉬운 방법은 아마도 도우미 매크로일 것입니다. 이 답변은 두 가지 접근 방식을 보여줍니다. 하나는 목록 매크로를 사용 하고 다른 하나는 도우미 매크로를 etoolbox사용하는 코드를 사용합니다 . 의 루프는 루프 변수의 값을 도우미 매크로에 대한 인수로 직접 전달하여 작동합니다.\expandafteretoolbox

\@namedef(즉, #2:\n) 의 첫 번째 인수에서 는 \n실제로 거기에 도달하도록 자동으로 확장됩니다 fi.


etoolbox다음은 목록 매크로를 사용하는 솔루션입니다 . 일부 설명은 인라인입니다. 이 접근 방식의 장점은 \n루프가 매크로(보통 도우미 매크로 필요)로 직접 구현되므로 루프 변수( )를 확장할 필요가 없다는 것입니다 .

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

\usepackage{etoolbox}

\makeatletter
% {<name>}{<lang>}
\newcommand*{\mlv@defaultvalue}[2]{%
  \csdef{#1@#2}{%
    {\scriptsize (Use {\ttfamily\textbackslash #1[#2]} to replace this text.)}}}
% {<repl. text>}{<name>}{<lang>}
% the unusual order instead of the more natural {<name>}{<lang>}{<repl. text>}
% makes for a more elegant call to \forcsvlist
\newcommand*{\mlv@realvalue}[3]{%
  \csdef{#2@#3}{#1}}

% [<langs>]{<name>}
% \forcsvlist{<macro>}{<item_1, item_2, ..., item_n>}
% calls <macro> once with each item_i as last argument
% <macro>{<item_1>}, <macro>{<item_2>}
% <macro> can already bring a few arguments of its own
% it could be <marco>{<fixed_1>}...{<fixed_n>} and then the invocations
% would become <marco>{<fixed_1>}...{<fixed_n>}{<item_1>} etc.
% Since the items are the <lang> argument it must be the last
% argument to \mlv@defaultvalue and \mlv@realvalue
\newcommand\MakeLocaleVar[2][en,fi]{%
  \forcsvlist{\mlv@defaultvalue{#2}}{#1}%
  \expandafter\newcommand\csname #2\endcsname[2][]{%
    \ifblank{##1}
      {\forcsvlist{\mlv@realvalue{##2}{#2}}{#1}}%
      {\mlv@realvalue{##2}{#2}{##1}}}%
  \expandafter\newcommand\csname Emit#2\endcsname[1][en]{\csuse{#2@##1}}%
}
\makeatother

\begin{document}

\MakeLocaleVar{Faculty}

% In main tex: set values
\Faculty{This faculty} % for all values
\Faculty[fi]{Tämä tiedekunta} % for a specific one

% In cls environments: use values
\EmitFaculty[en]
\EmitFaculty[fi]

% Now in addition I'd like to be able to:
\MakeLocaleVar[en,fi,de]{Gaculty}

\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]

\Gaculty{Foo}
\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]

\Gaculty[fi]{Föö}
\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]
\end{document}

이 교수진 Tämä Tieekunta//(이 텍스트를 바꾸려면 \Gaculty[en]을 사용하세요.) (이 텍스트를 바꾸려면 \Gaculty[fi]를 사용하세요.) (이 텍스트를 바꾸려면 \Gaculty[de]를 사용하세요.)//Foo Foo Foo //Foo Föö Foo


\foreach및 버전을 유지하려면 \ifthenelse도우미 기능을 사용할 수 있습니다.

비결은 \n실제 가치를 얻으려면 확장해야 한다는 것입니다. 이는 를 사용하여 가능 \expandafter하지만 많은 토큰을 뛰어넘기 위해 정밀하게 사용하지 않으려면 도우미 기능이 유용합니다. 나중 인수 대신 s를 사용하여 매크로의 첫 번째 인수를 확장하는 것이 약간 더 쉽기 때문에 \expandafter도우미 함수는 약간 예상치 못한 인수 순서를 사용합니다 {<lang>}{<name>}.

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

\usepackage{ifthen}
\usepackage{tikz}

\makeatletter
% helper for easier control of expansion
% {<lang>}{<name>}
\newcommand*{\mlv@helper}[2]{%
  \global\@namedef{#2:#1}{%
    {\scriptsize (Use {\ttfamily\textbackslash #2[#1]} to replace this text.)}%
  }%
}

% \n must be expanded to be useful.
% The first argument of \@namedef automatically expands it,
% but the second does not.
% Here we use a helper function to expand \n
% before it is processed.
\newcommand\MakeLocaleVar[2][en,fi]{%
    \foreach \n in {#1}{%
        \expandafter\mlv@helper\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}}%
}
\makeatother

\begin{document}

\MakeLocaleVar{Faculty}

% In main tex: set values
\Faculty{This faculty} % for all values
\Faculty[fi]{Tämä tiedekunta} % for a specific one

% In cls environments: use values
\EmitFaculty[en]
\EmitFaculty[fi]

% Now in addition I'd like to be able to:
\MakeLocaleVar[en,fi,de]{Gaculty}

\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]

\Gaculty{Foo}
\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]

\Gaculty[fi]{Föö}
\EmitGaculty[en]
\EmitGaculty[fi]
\EmitGaculty[de]
\end{document}

참고: LaTeX2e에서는 두 글자로 된 글꼴 명령이 더 이상 사용되지 않기 때문에 \ttfamily대신에 를 사용했습니다 (\tthttps://texfaq.org/FAQ-2letterfontcmd,LaTeX에서 두 글자 글꼴 스타일 명령(\bf, \it, …)이 부활할 예정입니까?).

관련 정보