首先@egregxparse
对我关于 缩略范围的宏(页面、音乐小节),
如何定义一个类似的命令,可以处理不连续的引用(范围和系列)?
此命令的幻想版本将接受像 这样的输入\range{\measures}{3-4,7,+9-11}
并将其扩展为mm.~3--4, 7, and 9--1
。这将取决于序列逗号的使用设置和连词的语言(如果+
是输入)。
以下是@egreg 的出发点:
\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}
答案1
支持复杂范围的宏必须充分利用expl3
。以下是一个可能的解决方案:
\documentclass{article}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\range}{ m m }
{
\cashner_range:nn { #1 } { #2 }
}
\tl_new:N \l__cashner_range_input_tl
\seq_new:N \l__cashner_range_input_seq
\bool_new:N \l__cashner_range_multi_bool
\cs_new_protected:Npn \cashner_range:nn #1 #2
{
% store the second argument in a token list
\tl_set:Nn \l__cashner_range_input_tl { #2 }
% change all -- into -
\tl_replace_all:Nnn \l__cashner_range_input_tl { -- } { - }
% change all - into -- (so as to normalize them)
\tl_replace_all:Nnn \l__cashner_range_input_tl { - } { -- }
% split the input at spaces (or whatever the second argument tells
\seq_set_split:NnV \l__cashner_range_input_seq { ~ } \l__cashner_range_input_tl
% set the “multi” boolean to false
\bool_set_false:N \l__cashner_range_multi_bool
\int_compare:nTF { \seq_count:N \l__cashner_range_input_seq == 1 }
{% if the sequence has just one item check if `-` is in the input
% and, in this case, set the boolean to true
\tl_if_in:nnT { #2 } { - } { \bool_set_true:N \l__cashner_range_multi_bool }
}
{% more than one item, set the boolean to true
\bool_set_true:N \l__cashner_range_multi_bool
}
% if the boolean is true, use the plural version of the abbreviation
\bool_if:NTF \l__cashner_range_multi_bool
{ #1{p} } { #1{s} }\nobreakspace
% use the sequence (with "and" between just two items,
% a comma between all items except the last one
% with ", and" between the last two, if more than two)
\seq_use:Nnnn \l__cashner_range_input_seq { ~and~ } { ,~ } { ,~and\nobreakspace }
}
\cs_generate_variant:Nn \seq_set_split:Nnn { NnV }
\NewDocumentCommand{\defineabbreviation}{ m m m }
{% define #1 to produce the singular version if the argument
% is p, or singular if the argument is s
\cs_new_protected:Npn #1 ##1
{
\str_case:nn{##1}{{s}{#2}{p}{#3}}
}
}
\ExplSyntaxOff
\defineabbreviation{\lines}{l.}{ll.}
\begin{document}
\range{\lines}{1}
\range{\lines}{1-2}
\range{\lines}{1 3}
\range{\lines}{1--2 3 4}
\range{\lines}{1 2 3-5}
\end{document}
请注意,您可以将范围输入为1--2
或1-2
,因此即使手指滑了一下也没有关系(但1---2
没有好处)。
输入首先存储在一个标记列表中,其中--
被归一化为-
并且-
被归一化回--
,因此在末尾范围将具有--
,与输入无关。完成此初步步骤后,输入在空格处被拆分为一个序列。
如果序列有多个元素或唯一元素包含连字符,则布尔值设置为 true。如果布尔值为 true,则使用参数 调用第一个参数中的宏p
,否则使用s
。此类宏应通过 定义\defineabbreviation
,并且不应在其他地方使用,除非您添加合适的参数(因此\lines{p}
是合法的并且会产生ll.\nobreakspace
)。
打印缩写(单数或复数形式)后,使用该序列。
如果你喜欢这样的语法
\range{\lines}{1-2,3, 4}
这只是改变路线的问题
\seq_set_split:NnV \l__cashner_range_input_seq { ~ } \l__cashner_range_input_tl
进入
\seq_set_split:NnV \l__cashner_range_input_seq { , } \l__cashner_range_input_tl
如果我们想提供本地化怎么办babel
? 这里就是。
\documentclass{article}
\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage[italian,english]{babel}
\usepackage{xparse}
\ExplSyntaxOn
\NewDocumentCommand{\range}{ m m }
{
\cashner_range:nn { #1 } { #2 }
}
\tl_new:N \l__cashner_range_input_tl
\tl_new:N \l_cashner_range_sep_two_tl
\tl_new:N \l_cashner_range_sep_many_tl
\tl_new:N \l_cashner_range_sep_last_tl
\seq_new:N \l__cashner_range_input_seq
\bool_new:N \l__cashner_range_multi_bool
\cs_new_protected:Npn \cashner_range:nn #1 #2
{
\tl_set:Nn \l__cashner_range_input_tl { #2 }
\tl_replace_all:Nnn \l__cashner_range_input_tl { -- } { - }
\tl_replace_all:Nnn \l__cashner_range_input_tl { - } { -- }
\bool_set_false:N \l__cashner_range_multi_bool
\seq_set_split:NnV \l__cashner_range_input_seq { ~ } \l__cashner_range_input_tl
\int_compare:nTF { \seq_count:N \l__cashner_range_input_seq == 1 }
{
\tl_if_in:nnT { #2 } { - } { \bool_set_true:N \l__cashner_range_multi_bool }
}
{
\bool_set_true:N \l__cashner_range_multi_bool
}
\bool_if:NTF \l__cashner_range_multi_bool
{ #1{p} } { #1{s} }\nobreakspace
\seq_use:NVVV \l__cashner_range_input_seq
\l_cashner_range_sep_two_tl
\l_cashner_range_sep_many_tl
\l_cashner_range_sep_last_tl
}
\cs_generate_variant:Nn \seq_set_split:Nnn { NnV }
\cs_generate_variant:Nn \seq_use:Nnnn { NVVV }
\NewDocumentCommand{\defineabbreviation}{ o m m m }
{
\IfNoValueTF { #1 }
{
\cs_new_protected:Npn #2 ##1
{
\str_case:nn{##1}{{s}{#3}{p}{#4}}
}
}
{
\exp_args:Nc \addto { extras#1 }
{
\cs_set_protected:Npn #2 ##1
{
\str_case:nn{##1}{{s}{#3}{p}{#4}}
}
}
}
}
\NewDocumentCommand{\setrangeseparators}{ommm}
{
\IfNoValueTF{#1}
{
\tl_set:Nn \l_cashner_range_sep_two_tl { #2 }
\tl_set:Nn \l_cashner_range_sep_many_tl { #3 }
\tl_set:Nn \l_cashner_range_sep_last_tl { #4 }
}
{
\exp_args:Nc \addto { extras#1 }
{
\tl_set:Nn \l_cashner_range_sep_two_tl { #2 }
\tl_set:Nn \l_cashner_range_sep_many_tl { #3 }
\tl_set:Nn \l_cashner_range_sep_last_tl { #4 }
}
}
}
\ExplSyntaxOff
% no argument means the default language
\setrangeseparators{ and }{, }{, and~}
\setrangeseparators[italian]{ e }{, }{ e~}%
\defineabbreviation{\lines}{l.}{ll.}
\defineabbreviation[italian]{\lines}{r.}{rr.}%
\begin{document}
\range{\lines}{1}
\range{\lines}{1-2}
\range{\lines}{1 3}
\range{\lines}{1--2 3 4}
\range{\lines}{1 2 3-5}
\selectlanguage{italian}
\range{\lines}{1}
\range{\lines}{1-2}
\range{\lines}{1 3}
\range{\lines}{1--2 3 4}
\range{\lines}{1 2 3-5}
\end{document}