交换两个数组元素

交换两个数组元素

我是 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。创建时数组的名称不会被记住。

相关内容