我是 Latex 编程的初学者。我正在开发一个基于 arrayjobx 的小包。我已经创建了结构来处理关联数组,如 arrayjobx.pdf 中所述。我的目的是添加一些高级功能,但我在创建宏时遇到了第一个问题,该宏会交换同一数组的两个元素的值。显示错误消息“未定义的控制序列”。欢迎任何帮助。
该包名为 arrayassoc.sty,其代码如下:
\NeedsTeXFormat{LaTeX2e}[1994/06/01] \提供包{数组关联} [2016/08/11 arrayassoc 包 v1.0] \RequirePackage{arrayjobx,multido} \制作字母 % 在内部,我们使用三个“标准”数组来定义一个关联数组 \newarray\AssociativeArray@名称 \newarray\AssociativeArray@Values \newarray\AssociativeArray@Indexes \newcount\AssociativeArrayNbValues \AssociativeArrayNbValues=\z@ \newif\ifAssociativeArray@ElementFound % 存储一个元素 \def\AssociativeArray(#1)=#2{% \expandarrayelementtrue \AssociativeArray@ElementFoundfalse \edef\@tempa{#1}% \edef\@tempb{#2}% \Multido{\iValue=\@ne+\@ne}{\AssociativeArrayNbValues}{% \checkAssociativeArray@Names(\iValue)% \ifx\@tempa\cachedata % 此元素已存在:我们替换其当前值 % \checkAssociativeArray@Values(\iValue)% 调试 % \typeout{在 #1 中,将 '\cachedata'\space 替换为 '#2'}% Debug \AssociativeArray@Values(\iValue)={\@tempb}% \AssociativeArray@ElementFoundtrue \多点停止 \fi} \如果关联数组@元素找到 \别的 % 新元素: \advance\AssociativeArrayNbValues\@ne \AssociativeArray@名称(\AssociativeArrayNbValues)={\@tempa}% \AssociativeArray@Values(\AssociativeArrayNbValues)={\@tempb}% \AssociativeArray@Indexes(\AssociativeArrayNbValues)={\the\AssociativeArrayNbValues}% \fi} % 获取一个元素 \def\checkAssociativeArray(#1){% \edef\@tempa{#1}% \edef\@tempb{999999}% \Multido{\iValue=\@ne+\@ne}{\AssociativeArrayNbValues}{% \checkAssociativeArray@Names(\iValue)% \ifx\@tempa\cachedata % 我们通过名字找到了它 \edef\@tempb{\iValue}% \多点停止 \fi} % 我们现在要得到它的值 \checkAssociativeArray@Values(\@tempb)} % 获取一个索引 \def\checkiAssociativeArray(#1){% \edef\@tempa{#1}% \edef\@tempb{999999}% \Multido{\iValue=\@ne+\@ne}{\AssociativeArrayNbValues}{% \checkAssociativeArray@Names(\iValue)% \ifx\@tempa\cachedata % 我们通过名字找到了它 \edef\@tempb{\iValue}% \多点停止 \fi} % 我们现在要获取它的索引 \checkAssociativeArray@Indexes(\@tempb)% %如果将上一行替换为:\@tempb,结果相同 } % 简单宏打印所有关联数组 \def\printAssociativeArray{% \par\noindent% 数组(\newline \multido{\iValue=\@ne+\@ne}{\AssociativeArrayNbValues}{% \checkAssociativeArray@Names(\iValue)% \hphantom{Ar}\iValue: ['\cachedata'] $=>$'\AssociativeArray@Values(\iValue)'\newline}% )\par% } % 一个简单的宏来打印索引数组(用于调试) \def\printIndexes{% \par\noindent% 数组(\newline \multido{\iIndex=\@ne+\@ne}{\AssociativeArrayNbValues}{% \checkAssociativeArray@Indexes(\iIndex)% \hphantom{Ar}index(\iIndex) $=$'\cachedata'\newline}% )\par% } \def\echoAssociativeArray(#1){% \检查关联数组(#1)% \如果空数据% \null\放松% \别的% \缓存数据% \fi% } \def\echoiAssociativeArray(#1){% \checkiAssociativeArray(#1)% \如果空数据% 0% 索引未找到 \别的% \缓存数据% \fi% } \def\firstValueAssociativeArray{% \AssociativeArray@值(1) } \def\firstKeyAssociativeArray{% \AssociativeArray@名称(1) } \def\lastValueAssociativeArray{% \AssociativeArray@Values(\AssociativeArrayNbValues) } \def\lastKeyAssociativeArray{% \AssociativeArray@名称(\AssociativeArrayNbValues) } \def\lengthAssociativeArray{% \the\AssociativeArrayNbValues } \newif\ifAssociativeArray@FirstElementFound \newif\ifAssociativeArray@SecondElementFound \def\swapAssociativeArray(#1)(#2){% \expandarrayelementtrue% \def\@tempi{\echoAssociativeArray(#1)}% \def\@tempj{\echoAssociativeArray(#2)}% \@tempi, \@tempj% 用于调试 \AssociativeArray(#1)={\@tempj}% 这不起作用 \AssociativeArray(#2)={\@tempi}% 这不起作用 %我把上面的两行代码替换成了下面的代码,但是不起作用。 %\AssociativeArray@FirstElementFoundfalse% %\AssociativeArray@SecondElementFoundfalse% %\edef\@tempa{#1}% %\edef\@tempb{#2}% %\Multido{\iValue=\@ne+\@ne}{\AssociativeArrayNbValues}{% % \checkAssociativeArray@Names(\iValue)% %\ ifx \ @tempa \ cachedata % % 第一个元素存在:我们替换其当前值 % \AssociativeArray@Values(\iValue)={\@tempj}% 此命令失败 % \AssociativeArray@FirstElementFoundtrue % \别的 %\ ifx \ @tempb \缓存数据 % % 第二个元素存在:我们替换其当前值 % \AssociativeArray@Values(\iValue)={\@tempi}% 此命令失败 % \AssociativeArray@SecondElementFoundtrue % \fi % \fi % \ifAssociativeArray@FirstElementFound % \如果AssociativeArray@SecondElementFound % \multidostop % \fi % \fi} } \makeatother \newcommand{\newAssociativeArray}[1]{% \global\expandafter\let\csname#1\endcsname\AssociativeArray% \global\expandafter\let\csname#1Check\endcsname\checkAssociativeArray% \global\expandafter\let\csname#1Checki\endcsname\checkiAssociativeArray% \global\expandafter\let\csname#1Print\endcsname\printAssociativeArray% \global\expandafter\let\csname#1Printi\endcsname\printIndexes% \global\expandafter\let\csname#1Echo\endcsname\echoAssociativeArray% \global\expandafter\let\csname#1Echoi\endcsname\echoiAssociativeArray% \global\expandafter\let\csname#1First\endcsname\firstValueAssociativeArray% \global\expandafter\let\csname#1Last\endcsname\lastValueAssociativeArray% \global\expandafter\let\csname#1FirstKey\endcsname\firstKeyAssociativeArray% \global\expandafter\let\csname#1LastKey\endcsname\lastKeyAssociativeArray% \global\expandafter\let\csname#1Length\endcsname\lengthAssociativeArray% \global\expandafter\let\csname#1Swap\endcsname\swapAssociativeArray% } \结束输入 %% %% 文件“arrayassoc.sty”结束。
MWE 是:
\documentclass{文章} \usepackage{arrayassoc} \开始{文档} \newAssociativeArray{单词} \word(a)={a} \word(b)={b} \wordSwap(a)(b) \word打印 \结束{文档}
答案1
让我从几个层面来解决你的问题,首先讨论最抽象(也是最重要的)的层面。
正如你所说,你是 TeX 编程的新手。尽管 TeX 上的一些软件包可能给人一种普通编程语言的印象,但它却截然不同。这意味着,如果你不理解 TeX 的一些关键概念,不学习一些关键技术,你就不会在像你这样的项目中取得很大进展。我认为最好的来源仍然是TeXbook;你不需要了解所有内容,但要深入研究。之后,你将通过查看其他人的代码来学习。
要分析您的问题,插入
\tracingmacros=2
在导致问题的行之前,即\wordSwap(a)(b)
。运行 TeX,在发生错误时中止它,然后检查日志文件。在那里你会看到\AssociativeArray
以第二个参数展开\@tempj
。因此定义的第四行\AssociativeArray
是
\edef\@tempb{\@tempj}
现在,重要的是要了解它\edef
做什么,不做什么。它会一直扩展,\@tempj
直到只剩下不可扩展的标记(受中描述的规则支配)TeXbook)。这样,标记可能会在正常执行期间不会被扩展,或者可能被设置为不同的值。\edef
不会“运行”参数,然后将结果填充到宏中\@tempb
。
在您的情况下,\@tempj
扩展为\echoAssociativeArray
,扩展为\checkAssociativeArray
(除其他内容外),扩展为\Multido
,扩展为\multido@
,扩展为\multido@@
,扩展为\multido@initvar
。现在\multido@temp
扩展 (在正常执行期间不会发生,仅由 检查\ifx
) 意外地是\@nil
。进一步扩展失败,因为\@nil
未定义。
定义交换的一种方法(至少对于简单情况)如下:
\def\swapAssociativeArray(#1)(#2)
{\checkAssociativeArray(#1)%
\let\@tmpa\cachedata
\checkAssociativeArray(#2)%
\let\@tmpb\cachedata
\AssociativeArray(#1)={\@tmpb}%
\AssociativeArray(#2)={\@tmpa}%
}
最后说明:所有关联数组实际上都指向同一个数组吗?如果您通过 创建一个“新”数组\newAssociativeArray{anotherword}
,\anotherwordEcho(a)
则将执行与 相同的操作\wordEcho(a)
,因为两者都\let
指向\echoAssociativeArray
。创建时数组的名称不会被记住。