我希望有一种语义化、可配置的方式来输入页码范围、诗行和音乐小节数。使用宏可以确保所有这些范围的格式一致,并允许我根据需要更改格式,例如,从“小节”更改为“小节”或(西班牙语)“compases”,或从“ll.”更改为“行”或“líneas”。
(看起来,各种软件包的作者bibleverse
已经以不同的方式处理了类似的挑战。)
现在我有宏\loc
并\loca
在下面的 MWE 中定义。我必须使用不同的宏来表示复数缩写,因为我不知道如何测试参数以查看它是否是范围。我更愿意将范围类型(条形图、线条、页面)作为宏包含,而不是逐字输入。
理想的情况可能是这样的:
命令:
\newcommand{\range}[2]{#1~#2}
% if #2 has more than one number, then do #1--#2
\newcommand{\measures}{mm.}
% except I would like the abbreviation to vary based on
% whether the number is plural (m. vs. mm.)
\newcommand{\lines}{ll.}
用法:
\range{\lines}{15}
--> 扩展为l.~15
因为只有一个数字
\range{\measures}{3 4}
--> 扩展为mm.~3--4
因为有两个数字
这可能比我想象的要复杂得多,如果是这样,请随时向我指出相关的参考资料,以便我自己解决这个问题。
尽管价值不大,但我可以用 C 语言设计出一个基本算法,下面也包含该算法。
\documentclass{article}
% Ranges of measures, lines (Latin locus, place; loca, region)
\newcommand{\loc}[2]{#1.~#2} % e.g., m.~13
\newcommand{\loca}[2]{#1#1.~#2} % e.g., mm.~13--14
% Or I could do this
\newcommand{\measure}[1]{m.~#1}
\newcommand{\measures}[1]{mm.~#1}
\newcommand{\poemline}[1]{l.~#1}
\newcommand{\poemlines}[1]{ll.~#1}
\begin{document}
At \loca{l}{13--14} in the poem, the composer puts accents on the wrong syllables (\loca{m}{44--46}).
At \poemlines{15--16} the composer puts accents on the right syllables (\measures{47--48}).
\end{document}
/* range.c
* Usage: range <rangetag> <start> <end>
* Examples:
* range measures 13 14 --> mm.~13--14
* range measures 13 --> m.~13
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MEASURETAG 'm'
#define LINETAG 'l'
#define TAGPUNCT '.'
int main(int argc, char *argv[])
{
char rangetag;
char tagpunct = TAGPUNCT;
if ((argc > 4) || (argc < 3)) {
fprintf(stderr, "Incorrect number of arguments.\n");
exit(EXIT_FAILURE);
}
if (strcmp(argv[1], "measures") == 0)
rangetag = MEASURETAG;
else if (strcmp(argv[1], "lines") == 0)
rangetag = LINETAG;
else {
fprintf(stderr, "Unknown range label.\n");
exit(EXIT_FAILURE);
}
if (argc == 4) /* If there are start and end range arguments */
printf("%c%c%c~%s--%s\n",
rangetag, rangetag, tagpunct, argv[2], argv[3]);
else /* If only one range argument */
printf("%c%c~%s\n", rangetag, tagpunct, argv[2]);
return(0);
}
答案1
这很容易xparse
:
\documentclass{article}
\usepackage{xparse}
\NewDocumentCommand{\range}{ m >{\SplitArgument{1}{ }}m }
{%
\dorange{#1}#2%
}
\NewDocumentCommand{\dorange}{ m m m }
{%
\IfNoValueTF{#3}{#1~#2}{#1[]~#2--#3}%
}
\NewDocumentCommand{\defineabbreviation}{ m m m }
{%
\NewDocumentCommand{#1}{o}{\IfNoValueTF{##1}{#2}{#3}}%
}
\defineabbreviation{\lines}{l.}{ll.}
\defineabbreviation{\measures}{m.}{mm.}
\begin{document}
\range{\lines}{1}
\range{\lines}{2 3}
\range{\measures}{4}
\range{\measures}{5 6}
\end{document}
该\range
命令吸收第一个参数,并在空格处分割第二个参数,返回{<before space>}{<after space>}
用作#2
。如果没有空格,<after space>
则为“NoValue”(特殊信号)。
然后我们将控制权交给\dorange
第一个检查其第三个参数是否为“NoValue”(无范围情况)并调用
#1~#2
否则它会调用
#1[]~#2
这里#1
应该有一个用\defineabbreviation
; 输入定义的宏
\defineabbreviation{\lines}{l.}{ll.}
实际执行
\NewDocumentCommand{\lines}{o}{\IfNoValueTF{#1}{l.}{ll.}}
所以当\dorange
调用时\lines~{1}
结果将是
l. 1
当它调用时\lines[]~2--3
结果将是
2–3
(因为空的可选参数不是“NoValue”)。