
Estou tentando criar muitos comandos diferentes com base em um único comando (essencialmente um cabeçalho personalizado) de maneira previsível. Existe uma maneira de fazer isso em TeX/LaTeX?
Meu exemplo específico:
Estou escrevendo uma especificação de módulo, onde cada um dos subtítulos é indicado por \modulespec{description}{the description}
ou \modulespec{learning outcomes}{the learning outcomes}
etc. Espero que o TeX possa gerar comandos como \mdescription{the description}
e\mlearningoutcomes{the learning outcomes}
Eu sei que em uma expansão semelhante ao bash combinada com o TeX eu escreveria
for i in 'description' 'learning outcomes'
do
\def\csname m\getRidOfSpaces{$i} \endcsname{\modulespec{$i}{#1}}
done
(se você me desculpar a combinação de idiomas) onde \getRidOfSpaces
muda learning outcomes
para learningoutcomes
. Isso é possível/viável com TeX/LaTeX?
M(não)NÓS:
\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}
Responder1
Aqui está uma implementação do 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}
Alguns comentários sobre o código.
Definimos uma macro de nível de usuário para definir todos os módulos de uma só vez; isto é \definemodules
, simplesmente entrega o controle à função de nível do programador \farley_definemodules:n
(esta é a melhor prática de acordo com as diretrizes de programação do LaTeX3).
Agora a função \farley_definemodules:n
divide seu argumento entre vírgulas e faz um loop sobre os itens. O item atual está disponível como #1
, mas precisamos ##1
porque estamos dentro de uma definição.
Cada ciclo armazena o item na variável da lista de tokens temporários \l_farley_temp_tl
para que possamos aplicá \tl_replace_all:Nnn
-lo. A substituição é “qualquer espaço é substituído por nada”; no ambiente de programação os espaços são ignorados, portanto um “espaço real” é denotado por ~
.
Agora usamos a \cs_new_protected:cpn
função que é análoga ao estilo antigo \@namedef
: o primeiro argumento é transformado em um nome de sequência de controle com \csname...\endcsname
(a lista de tokens é expandida aqui pela regra do TeX).
Responder2
Parece que você deseja fazer um loop 'for each'. O kernel do LaTeX possui uma macro \@for
que faz isso para cada elemento de uma lista separada por vírgula. Você também precisa \zap@space
ou similar para remover espaços. Por exemplo:
\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
definirá \mdescription
e \mlearningoutcomes
conforme necessário. Usei um \edef
para forçar a expansão da variável nos lugares corretos. (Não adianta aplicar \protected@edef
aqui, pois o 'texto' deve ser 'seguro' por dentro \csname
.)
Responder3
Usando \xintFor
os utilitários do kernel LaTeX2e (com um @
em seus nomes):
\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}