我正在重写我为 XeLaTeX 编写的一个包来处理 unicode 块。这意味着要处理从代码点 1234 开始到代码点 5678 结束的块“BlockName”中的所有字符的字符类设置。
现在我有一个宏,它接受三个输入,并将它们转换为:
\newcounter{glyphcounter}
\newcommand{\@defineUnicodeClass}[3]{
\newXeTeXintercharclass#1
\forloop{glyphcounter}{#2}{\value{glyphcounter}<#3}\XeTeXcharclass\value{glyphcounter}=#1}
\XeTeXcharclass#3=#1}
然后它会被调用很多次:
\@defineUnicodeClass{\AegeanNumbersClass}{65792}{65855}
\@defineUnicodeClass{\AlphabeticPresentationFormsClass}{64256}{64335}
\@defineUnicodeClass{\AncientGreekMusicalNotationClass}{119296}{119375}
...
我希望以这样一种方式来压缩它,以便我可以简单地定义块列表,然后{{AegeanNumbers,65792,65855},{AlphabeticPresentationForms,64256,64335},{AncientGreekMusicalNotation,119296,119375},...}
运行此列表来为每个块生成所有内容。
我正在考虑将数据定义写为{AegeanNumbers,65792,65855,AlphabeticPresentationForms,64256,64335,AncientGreekMusicalNotation,119296,119375,...}
,因此只是一个连接的字符串,其中每个第一个 %3 项都是一个新的块定义,然后有一个宏检查输入是否为空,如果不是,则取该列表中的前三个项,处理它们,然后使用列表“尾部”进行递归,但我在 TeX 中实现这一点很难。
如果这是最好的方法,我该怎么做?如果不是,有什么更好的方法来处理这类数据?
我看了http://maraist.org/comma-separated-lists-in-latex_08-2009弄清楚如何实现头部/尾部分离,但我在 XeLaTeX 中没有成功。我尝试过
\def\myTestCommand#1,#2{#1}
只是想看看它是否会做些什么,但是用“\myTestCommand{a,b,c,d}”调用它只会导致“失控参数?”错误。
我还查看了\forcsvlist
来自电子工具箱包,但据我所知,它只能理解逗号分隔的列表,其中每个元素都是同一类东西,并且没有元组的概念。我可以编写一个带有两个状态变量宏的切换宏来实现“如果状态为空,则填充第一个变量,如果一个已填充,则填充第二个变量,如果两个都已填充,则调用以下宏并清空两个变量”,但如果有人有更简洁的方法,那就更好了。
答案1
这里有两种方法可以处理命令的定义以及迭代。两者都需要定义一个可以定义命令的宏:
\def\CommandFactory#1#2#3{%
\expandafter\gdef\csname#1\endcsname{#1 class}
\expandafter\gdef\csname#1start\endcsname{#2}
\expandafter\gdef\csname#1end\endcsname{#3}
}
这可以看作是定义objects
或 的实用程序struct
。在上面三个命令中,例如aegean
、aegeanstart
和aegeanend
。如果使用点或 ,@
结构会更明显,aegean@start
、aegean@end
。
现在回到迭代,第一个方法使用\@elt
,它是元素的缩写,以及Lisp 遗迹在 LaTeX 中发现。
\def\@elt#1,#2,#3,{\CommandFactory{#1}{#2}{#3}}
\def\unicoderange{\@elt test,1,2,\@elt testi,2,3,\@elt testii,4,5,}
这是 Knuth 处理列表的方式(他使用\\
而不是\@elt
)。
处理迭代的第二种方法是使用 LaTeX,@for
下面显示了一个极简示例。我曾经用\i
它来使它更直观,因此请注意通过组运行代码或保存并恢复其定义,否则无点 i 将消失!
\documentclass{article}
\begin{document}
\def\CommandFactory#1#2#3{%
\expandafter\gdef\csname#1\endcsname{#1 class}
\expandafter\gdef\csname#1start\endcsname{#2}
\expandafter\gdef\csname#1end\endcsname{#3}
}
\def\unicodeClasses{aegean,musical,alphabetic}
\CommandFactory{aegean}{65792}{65855}
\CommandFactory{musical}{119296}{119375}
\CommandFactory{alphabetic}{64256}{64335}
\makeatletter
\@for \i:=\unicodeClasses \do{
\csname\i\endcsname,
\csname\i start\endcsname-%
\csname\i end\endcsname,
}
%% another way to iterate using @elt
\def\@elt#1,#2,#3,{\CommandFactory{#1}{#2}{#3}}
\def\unicoderange{\@elt test,1,2,\@elt testi,2,3,\@elt testii,4,5,}
\unicoderange
\testi
\makeatother
\end{document}
答案2
我不知道 LaTeX 的循环宏,但这是在 ConTeXt 中处理嵌套逗号列表的方法
\def\definecharacterlist
{\dosingleargument\dodefinecharacterlist}
\def\dodefinecharacterlist[#1]%
{\processcommalist[#1]\defineUnicodeCharacter}
\def\defineUnicodeCharacter#1%
{\dostartdefineUnicodeCharacter#1\dostopdefineUnicodeCharacter}
\def\dostartdefineUnicodeCharacter#1,#2,#3\dostopdefineUnicodeCharacter
{\writestatus{DEBUG}{1: #1; 2: #2; 3: #3}}
\definecharacterlist[{AegeanNumbers,65792,65855},{AlphabeticPresentationForms,64256,64335},{AncientGreekMusicalNotation,119296,119375}]
这\dosingleargument
只是 ConTeXt 处理可选参数的方式。您可以\processcommalist
用等效的 LaTeX 循环宏替换,也可以\writestatus
用您想要的任何宏替换。
答案3
语法如下
\@defineUnicodeclasses{AegeanNumbers/65792/65855,
AlphabeticPresentationForms/64256/64335,
AncientGreekMusicalNotation/119296/119375}
是好的吗?我想是的。那么
\def\@defineUnicodeclasses#1{%
\def\@Unicodeclasseslist{#1}%
\@for\next:=\@Unicodeclasseslist\do
{\expandafter\do@Unicodeclasslist\next\@nil}}
\def\do@Unicodeclasslist#1/#2/#3\@nil{%
\count@=\numexpr#2-1\relax
\expandafter\newXeTeXintercharclass\csname#1\endcsname
\loop\ifnum\count@<#3\relax
\advance\count@\@ne
\XeTeXcharclass\count@=\csname#1\endcsname
\repeat}
应该是您所需要的。但我看不出比多个\@defineUnicodeclass
声明有什么优势。