我的具体例子:

我的具体例子:

我正在尝试以可预测的方式基于单个命令(本质上是自定义标头)创建许多不同的命令。有没有办法在 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 outcomeslearningoutcomes。使用 TeX/LaTeX 是否可行?

M(不)WE:

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

定义循环

相关内容