Мой конкретный пример:

Мой конкретный пример:

Я пытаюсь создать много разных команд на основе одной команды (по сути, пользовательского заголовка) предсказуемым образом. Есть ли способ сделать это в TeX/LaTeX?

Мой конкретный пример:

Я пишу спецификацию модуля, где каждый из подзаголовков обозначается как \modulespec{description}{the description}или \modulespec{learning outcomes}{the learning outcomes}и т.д. Я надеюсь, что TeX сможет генерировать команды типа \mdescription{the description}и\mlearningoutcomes{the learning outcomes}

Я знаю, что в bash-подобном расширении в сочетании с TeX я бы написал

for i in 'description' 'learning outcomes'
do
    \def\csname m\getRidOfSpaces{$i} \endcsname{\modulespec{$i}{#1}}
done

(если вы извините за сочетание языков) где \getRidOfSpacesизменения learning outcomesв learningoutcomes. Возможно ли это с TeX/LaTeX?

М(не)МЫ:

\documentclass{article}
\usepackage{blindtext}

% In the real thing, this definition is much more complicated
\newcommand{\modulespec}[2]{\section{#1} #2}

% The code I want in pure TeX/LaTeX
for i in 'description' 'learning outcomes'
do
    \def\csname m\getRidOfSpaces{$i} \endcsname{\modulespec{$i}{#1}}
done
% end of non TeX code


\begin{document}

% Both work
\modulespec{description}{\blindtext}
\modulespec{learning outcomes}{\blindtext}

% These two commands should produce the same output as the previous two
\mdescription{\blindtext}
\mlearningoutcomes{\blindtext}

\end{document}

решение1

Вот реализация LaTeX3.

\documentclass{article}
\usepackage[margin=1cm]{geometry} % to fit in one page
\usepackage{blindtext}
\usepackage{xparse}

\newcommand{\modulespec}[2]{\section{#1} #2}

\ExplSyntaxOn

\NewDocumentCommand{\definemodules}{m}
 {
  \farley_definemodules:n { #1 }
 }

\tl_new:N \l_farley_temp_tl
\cs_new_protected:Npn \farley_definemodules:n #1
 {
  \clist_map_inline:nn { #1 }
   {
    \tl_set:Nn \l_farley_temp_tl { ##1 }
    \tl_replace_all:Nnn \l_farley_temp_tl { ~ } { }
    \cs_new_protected:cpn { m \l_farley_temp_tl } { \modulespec { ##1 } }
   }
 }

\ExplSyntaxOff

\definemodules{
  description,
  learning outcomes
}

\begin{document}

\modulespec{description}{\blindtext}
\modulespec{learning outcomes}{\blindtext}

% These two commands should produce the same output as the previous two
\mdescription{\blindtext}
\mlearningoutcomes{\blindtext}

\end{document}

введите описание изображения здесь

Некоторые комментарии по коду.

Мы определяем макрос уровня пользователя для определения всех модулей за один раз; он \definemodulesпросто передает управление функции уровня программиста \farley_definemodules:n(это лучшая практика согласно рекомендациям по программированию LaTeX3).

Теперь функция \farley_definemodules:nразделяет свой аргумент по запятым и выполняет цикл по элементам. Текущий элемент доступен как #1, но нам нужно, ##1потому что мы внутри определения.

Каждый цикл сохраняет элемент в переменной списка скретч-токенов, \l_farley_temp_tlчтобы мы могли применить \tl_replace_all:Nnnего. Замена — «любой пробел заменяется ничем»; в среде программирования пробелы игнорируются, поэтому «реальный пробел» обозначается как ~.

Теперь мы используем \cs_new_protected:cpnфункцию, которая является аналогом старого стиля \@namedef: первый аргумент преобразуется в имя управляющей последовательности с помощью \csname...\endcsname(список токенов здесь расширяется по правилу TeX).

решение2

Похоже, вы хотите сделать цикл 'for each'. В ядре LaTeX есть макрос \@for, который делает это для каждого элемента списка, разделенного запятыми. Вам также нужно \zap@spaceили похожее, чтобы удалить пробелы. Например:

\newcommand{\modulespec}[2]{\section{#1} #2}
\makeatletter
\@for\@tempa:=description,learning outcomes\do{%
  \edef\@tempa
    {\expandafter\expandafter\expandafter\zap@space
      \expandafter\@tempa\space\@empty}%
  \expandafter\edef\csname m\@tempa\endcsname
    {\noexpand\modulespec{\@tempa}}%
}
\makeatother

определит \mdescriptionи \mlearningoutcomesпо мере необходимости. Я использовал \edefдля принудительного расширения переменной в правильных местах. (Применять \protected@edefздесь бесполезно, так как «текст» должен быть «безопасным» внутри \csname.)

решение3

Использование \xintForи утилит ядра LaTeX2e (с символом @в названии):

\documentclass{article}
\usepackage[english]{babel}
\usepackage{blindtext}
\usepackage{xinttools}
% In the real thing, this definition is much more complicated
\newcommand{\modulespec}[2]{\section{#1} #2}

% % The code I want in pure TeX/LaTeX
% for i in 'description' 'learning outcomes'
% do
%     \def\csname m\getRidOfSpaces{$i} \endcsname{\modulespec{$i}{#1}}
% done
% % end of non TeX code

% Nota bene: in the above you probably meant \long\def, not \def
\makeatletter
\xintFor #1 in {description, learning outcomes}\do
{\long\@namedef{m\zap@space #1 \@empty}##1{\modulespec{#1}{##1}}}
\makeatother

\begin{document}

% Both work
\modulespec{description}{\blindtext}
\modulespec{learning outcomes}{\blindtext}

% These two commands should produce the same output as the previous two
\mdescription{\blindtext}
\mlearningoutcomes{\blindtext}

\end{document}

деф петля

Связанный контент