如何实现这样的宏:不定参数,先逐个分析并存储,决定排版方案,最后倒回去排版

2020-05-09 15:39发布

如何定义一个不定参数(可能超过9个)的宏:先读入所有参数,并分析,再根据分析结果采取不同策略处理各个参数问题比较抽象,先看一个简单例子:需要定义一个宏 \qq,其参数数量不定,可以是 1-无穷个(超过...

如何定义一个不定参数(可能超过9个)的宏:先读入所有参数,并分析,再根据分析结果采取不同策略处理各个参数


问题比较抽象,先看一个简单例子:


需要定义一个宏 \qq,其参数数量不定,可以是 1-无穷个(超过9),期望效果:

\qq{P1}{P2} -> (P1/2)(P2/2)
\qq{P1}{P2}……{Pn} -> (P1/n)(P2/n)……(Pn/n)


实现:

% 用户接口:初始化\P,并调底层 \storeP 实现:加工并存储各个参数到 \P 中,直到最后一个参数,再输出 \P
\def\qq{\def\P{\relax}\count6120=0\relax\storeP} 
\def\qq{\let\P=\relax \count6120=0\relax\storeP}

% \storeP 的实现代码(失败的教训):
尝试方案 1  (失败)
\def\storeP#1{\advance\count6120 by 1\let\oldP=\P \def\PP{\oldP  (#1/\the\count6120)}\let\P=\PP\@ifnextchar\bgroup{\storeP}{\P}}

尝试方案  2  (失败)
\def\storeP#1{\advance\count6120 by 1\def\newP{\P  (#1/\the\count6120)}\let\P=\PP\@ifnextchar\bgroup{\storeP}{\P}}

瞎尝试方案  x-1  (失败)
\def\storeP#1{\advance\count6120 by 1\let\oldP=\P
\edef\P{\noexpand\oldP (#1/\the\count6120)} \@ifnextchar\bgroup{\storeP}{\P}}

瞎尝试失败方案  x
\def\storeP#1{\advance\count6120 by 1\let\oldP=\P
\def\P{\noexpand\oldP (#1/\the\count6120)} \@ifnextchar\bgroup{\storeP}{\P}}

盲目骚操作  x + 1
% \edef  \xdef \expanderafter ……
初步成功!
\def\storeP#1{\advance\count6120 by 1\apptocmd{\P}{(#1/\the\count6120)}{\relax}{\relax}\@ifnextchar\bgroup{\storeP}{\P}}

\qq{P1}{P2}{P3}{P4}      【期望的结果为(P1/4)(P2/4)(P3/4)(P4/4)】

这个例子看起来没什么实用性,

那好,大家再看下这个抽象问题背后的实际场景:选择题的自动排版

场景分析:

选项数量不确定,可能有多个(甚至超过9个,你懂的),

需要先读入所有选项,进行分析:找出最长选项,并结合选项数量决定如何排版(一行排几个)


选项数可选排版方案:每行可能排多少选项
11


212

31
3
412
4
5123
6123
712
4
81234
9123
1012
4
111234
121234
……




麻烦之处:

① 参数不定,还可能是无穷

② 需要先分析并做决策,再倒回去排版。


遇到一个参数分析一个,并将参数存储到一个内部宏中



目前选项可以自动支持 2-9 个参数,但被一个 Python 工程师吐嘈说支持不了 10 个参数,暴力写做不到无穷多个,还搞得模板还很臭


参考:

如果只是想遍历所有的参数,不需要先来一遍分析,再下决策然后倒回去排版,还是挺好实现的。例如:分分钟可以写出求和宏:

\def\addnext#1{\advance\count6120 by #1\@ifnextchar\bgroup{\addnext}{\the\count6120}}
\def\sumup{\count6120=0\addnext}

效果
\sumup{2}{3}{5}{5}{2}{3}{5}{5}{2}{3}{5}{5}{2}{3}{5}{5}{2}{3}{5}{5}{2}{3}{5}{5}  ……


3条回答
zhaochongbin
2020-05-09 17:39

根据以下python的逻辑

S = 0
def sum(n=None):
    if n is None:
        return 0
    global S
    S = n
    def inner(m=None):
        global S
        if m is None:
            return S
        S += m
        return inner
    return inner

print(sum())
print(sum(1)())
print(sum(1)(2)())
print(sum(1)(2)(3)())
print(sum(1)(2)(3)(4)())

用LaTeX3可写出相似的逻辑

\documentclass{ctexart}
\ExplSyntaxOn
\int_new:N \g_S_int

\NewDocumentCommand {\sumup} {O{}} {
 \tl_if_blank:nTF {#1} {
  0
 }{
  \int_gset:Nn \g_S_int { #1 }
  \inner
 }
}

\NewDocumentCommand {\inner} {O{}} {
 \tl_if_blank:nTF {#1} {
  \int_to_arabic:n { \g_S_int }
 }{
  \int_gadd:Nn \g_S_int { #1 }
  \inner
 }
}
\ExplSyntaxOff

\begin{document}
\sumup

\sumup[1]

\sumup[1][2]

\sumup[1][2][3]

\sumup[1][2][3][4]
\end{document}

效果如下图

93f1d2a130575dd91028279647a9980.png

我并不想完全做出主问题,用你最后一个求和宏来举例,只是为了让你对LaTeX3的用法和能实现的东西有个初步了解。