我想尝试学习如何一次性定义大量宏。在聊天室问完这个问题后,我收到了来自一位很棒的埃格尔和玩。
这里是指向工作代码的链接。此后,我尝试进行概括,以便函数\definefoo
可以定义长度任意的宏,即存在统一的方式来定义\ab
、\abg
等等\abgd
……
经过更多讨论后我对其进行了修改。
我希望以下非工作代码也可以作为我想要实现的伪代码:
\documentclass{amsart}
\usepackage{amsmath}
\makeatletter
\def\definefoo#1#2#3#4{%
\ifx\relax#4%
\ifx\relax#3%
\ifx\relax#2%
\expandafter\def\csname#1#1\endcsname##1{%
\csname greek#1\endcsname_{##1}}
\fi
\expandafter\def\csname#1#2\endscname##1##2{%
\csname greek#1\endcsname_{##1}%
\csname greek#2\endcsname_{##2}}
\fi
\expandafter\def\csname#1#2#3\endscname##1##2##3{%
\csname greek#1\endcsname_{##1}%
\csname greek#2\endcsname_{##2}%
\csname greek#3\endcsname_{##3}}
\fi
\expandafter\def\csname#1#2#3#4\endscname##1##2##3##4{%
\csname greek#1\endcsname_{##1}%
\csname greek#2\endcsname_{##2}%
\csname greek#3\endcsname_{##3}%
\csname greek#4\endscname_{##4}}
}
\let\greeka\alpha
\let\greekb\beta
\let\greekg\gamma
\let\greekd\delta
\let\greekl\lambda
\let\greekm\mu
\definefoo a\relax\relax\relax
\definefoo ag\relax\relax\relax
\definefoo abg\relax
\definefoo abd\relax
\makeatother
\begin{document}
$\abd{ij}{kl}{mn} \aa{ij}{kl} \aa{kl}$
\end{document}
答案1
你需要一些\else
分支:
\documentclass{amsart}
\usepackage{amsmath}
\def\definefoo#1#2#3#4{%
\ifx\relax#4%
\ifx\relax#3%
\ifx\relax#2%
\expandafter\def\csname#1#1\endcsname##1{%
\csname greek#1\endcsname_{##1}}
\else
\expandafter\def\csname#1#2\endcsname##1##2{%
\csname greek#1\endcsname_{##1}%
\csname greek#2\endcsname_{##2}}
\fi
\else
\expandafter\def\csname#1#2#3\endcsname##1##2##3{%
\csname greek#1\endcsname_{##1}%
\csname greek#2\endcsname_{##2}%
\csname greek#3\endcsname_{##3}}
\fi
\else
\expandafter\def\csname#1#2#3#4\endcsname##1##2##3##4{%
\csname greek#1\endcsname_{##1}%
\csname greek#2\endcsname_{##2}%
\csname greek#3\endcsname_{##3}%
\csname greek#4\endcsname_{##4}}
\fi
}
\let\greeka\alpha
\let\greekb\beta
\let\greekg\gamma
\let\greekd\delta
\let\greekl\lambda
\let\greekm\mu
\definefoo a\relax\relax\relax
\definefoo ag\relax\relax
\definefoo abg\relax
\definefoo abd\relax
\makeatother
\begin{document}
$\abd{ij}{kl}{mn}$
$\ag{ij}{kl}$
$\aa{kl}$
\end{document}
我建议你采用不同的方法。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\prop_new:N \g_kan_greek_prop
\prop_gput:Nnn \g_kan_greek_prop { a } { alpha }
\prop_gput:Nnn \g_kan_greek_prop { b } { beta }
\prop_gput:Nnn \g_kan_greek_prop { g } { gamma }
\prop_gput:Nnn \g_kan_greek_prop { d } { delta }
%% Add all the equivalences you need
\NewDocumentCommand{\definefoo}{m}
{
\kan_definefoo:n { #1 }
}
\cs_new_protected:Npn \kan_definefoo:n #1
{
\int_case:nnn { \tl_count:n { #1 } }
{
{1}{ \kan_definefoo_one:n #1 }
{2}{ \kan_definefoo_two:nn #1 }
{3}{ \kan_definefoo_three:nnn #1 }
{4}{ \kan_definefoo_four:nnnn #1 }
}
{}
}
\cs_new_protected:Npn \kan_definefoo_one:n #1
{
\cs_new:cpx { S#1 } ##1
{
\exp_not:c { \prop_get:Nn \g_kan_greek_prop {#1} } \sb { ##1 }
}
}
\cs_new_protected:Npn \kan_definefoo_two:nn #1 #2
{
\cs_new:cpx { S#1#2 } ##1 ##2
{
\exp_not:c { \prop_get:Nn \g_kan_greek_prop {#1} } \sb { ##1 }
\exp_not:c { \prop_get:Nn \g_kan_greek_prop {#2} } \sb { ##2 }
}
}
\cs_new_protected:Npn \kan_definefoo_three:nnn #1 #2 #3
{
\cs_new:cpx { S#1#2#3 } ##1 ##2 ##3
{
\exp_not:c { \prop_get:Nn \g_kan_greek_prop {#1} } \sb { ##1 }
\exp_not:c { \prop_get:Nn \g_kan_greek_prop {#2} } \sb { ##2 }
\exp_not:c { \prop_get:Nn \g_kan_greek_prop {#3} } \sb { ##3 }
}
}
\cs_new_protected:Npn \kan_definefoo_four:nnnn #1 #2 #3 #4
{
\cs_new:cpx { S#1#2#3#4 } ##1 ##2 ##3 ##4
{
\exp_not:c { \prop_get:Nn \g_kan_greek_prop {#1} } \sb { ##1 }
\exp_not:c { \prop_get:Nn \g_kan_greek_prop {#2} } \sb { ##2 }
\exp_not:c { \prop_get:Nn \g_kan_greek_prop {#3} } \sb { ##3 }
\exp_not:c { \prop_get:Nn \g_kan_greek_prop {#4} } \sb { ##4 }
}
}
\ExplSyntaxOff
\definefoo{abgd}
\definefoo{abd}
\definefoo{ab}
\begin{document}
$\Sabgd{ij}{kl}{mn}{pq}$
$\Sabd{ij}{kl}{mn}$
$\Sab{ij}{kl}$
\end{document}
我认为,与其为了实现“单字母大小写”而将字母加倍,不如使用一个前缀,使已经定义的包更难被找到。
这是另一种方法:定义一个宏\foo
,以递归方式从其第一个参数中吃掉一个字符:
\documentclass{article}
\makeatletter
\def\foo#1{\foo@aux#1\@nil}
\def\foo@aux#1#2\@nil{%
\if\relax\detokenize{#2}\relax
\expandafter\@firstoftwo
\else
\expandafter\@secondoftwo
\fi
{\do@final@foo{#1}}
{\do@inner@foo{#1}{#2}}}
\def\do@final@foo#1#2{\csname greek#1\endcsname_{#2}}
\def\do@inner@foo#1#2#3{%
\csname greek#1\endcsname_{#3}%
\foo@aux#2\@nil}
\makeatother
\let\greeka\alpha
\let\greekb\beta
\let\greekg\gamma
\let\greekd\delta
\let\greekl\lambda
\let\greekm\mu
\begin{document}
$\foo{ab}{ij}{kl}$
$\foo{a}{ij}$
$\foo{aambd}{ij}{kl}{mn}{pq}{rs}$
\end{document}
推荐方式
在 LaTeX 中,使用具有可变数量参数的宏并不是最好的方法,因此这里是关于进行更可控的输入的最终建议。
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\foo}{mm}
{
\kan_foo:nn { #1 } { #2 }
}
\seq_new:N \l__kan_symbols_seq
\seq_new:N \l__kan_subscripts_seq
\cs_new_protected:Npn \kan_foo:nn #1 #2
{
\seq_set_from_clist:Nn \l__kan_symbols_seq { #1 }
\seq_set_from_clist:Nn \l__kan_subscripts_seq { #2 }
\seq_mapthread_function:NNN \l__kan_symbols_seq \l__kan_subscripts_seq
\__kan_print:nn
}
\cs_new:Npn \__kan_print:nn #1 #2
{
#1\sb{#2}
}
\ExplSyntaxOff
\begin{document}
$\foo{\alpha,\beta}{ij,kl}$
$\foo{a,\gamma,\delta}{ij,kl,mn}$
\end{document}
该宏\foo
以两个列表作为参数;第一个列表的每个元素与第二个列表中的对应元素配对。输入起来可能更无聊,但肯定更清晰。