
我試圖以可預測的方式基於單一命令(本質上是自訂標頭)創建許多不同的命令。有沒有辦法在 TeX/LaTeX 中做到這一點?
我的具體例子:
我正在編寫一個模組規範,其中每個子標題都用\modulespec{description}{the description}
或等\modulespec{learning outcomes}{the learning outcomes}
表示。\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 every”迴圈。 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
我使用了 an\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}