如何对 LaTeX 中的坐标列表进行字典排序?

如何对 LaTeX 中的坐标列表进行字典排序?

我需要对 TikZ 坐标列表进行排序,首先按增加 X 坐标,然后按增加 Y 坐标。目前,我通过调用外部 Python shell 来执行此操作,但如果能够完全在 LaTeX 中执行此操作会更好。

下面是一个最小的工作示例来说明:

\documentclass{article}

\def\coordList{(5,5) (0,0) (2,5) (2,4) (-1, 3)}
\def\sortList#1{
  % TODO: ?
}

\begin{document}
Input: \coordList

After sorting: \sortList{\coordList}

Expected result: (-1, 3) (0,0) (2,4) (2,5) (5,5)
\end{document}

列表的格式不一定需要与 MWE 中的格式相同;唯一的要求是它们最后应该进行排序。

答案1

更新添加了处理维度坐标的变体。

这是“代码4”的改编https://tex.stackexchange.com/a/273084/4686

我最初打算使用归并排序(上面链接中的“代码 6”),但、 和空格也(特别处理\pdfescapestring,因此选择了“代码 4”。这是可扩展的代码。即使由 TikZ 解析也应该可以工作。警告:我有一天注意到 TikZ 的坐标限制在大约 100 个扩展步骤,这对于任何严肃的可扩展宏来说都非常低。可以使用技巧。包中xintexpr有一个宏\xintthecoords,它将偶数个值的逗号分隔列表转换为 TikZ 的坐标对coordinates。也许我应该在这里编写代码,使其一直使用逗号分隔的值。这将很方便\xintthecoords在最后一分钟将其输入以生成坐标对,从而以欺骗 TikZ 的扩展计数器。我想得太晚了,如果需要,我会编辑。

\documentclass{article}

\makeatletter
% This is based ond the "code 4" of
% https://tex.stackexchange.com/a/273084/4686,
% 
% modified to handle pairs (a, b) (c,d)(e, f) (g,h) etc...
% 
% acts expandably. Lexicographic order.

\makeatletter

% Here we define the comparison macro for pairs (a,b)
% We assume decimal numbers acceptable to \ifdim tests

\long\def\xintdothis #1#2\xintorthat #3{\fi #1}%
\let\xintorthat \@firstofone

\long\def\@thirdoffour  #1#2#3#4{#3}%
\long\def\@fourthoffour #1#2#3#4{#4}%

\def\IfFirstPairIsGreaterTF #1#2{\@IfFirstPairIsGreaterTF #1,#2,}%

\def\@IfFirstPairIsGreaterTF #1,#2,#3,#4,{%
    \ifdim #1\p@=#3\p@
       \xintdothis{%
         \ifdim #2\p@>#4\p@\expandafter\@firstoftwo
         \else\expandafter\@secondoftwo\fi}\fi
    \ifdim #1\p@>#3\p@\expandafter\@thirdoffour
                      \else\expandafter\@fourthoffour\fi
    \xintorthat{}%
}%

% not needed for numerical inputs
% \catcode`! 3
% \catcode`? 3

% Here there is a very strange \romannumeral0\romannumeral0, this is
% due to some convoluted scheme to avoid double spaces or no spaces
% in between coordinate pairs. Trust me.
\def\QSpairs {\romannumeral0\romannumeral0\qspairs }%
% first we check if empty list
\def\qspairs   #1{\expandafter\qspairs@a\romannumeral-`0#1(!)(?)}%
\def\qspairs@a #1(#2{\ifx!#2\expandafter\qspairs@abort\else
                        \expandafter\qspairs@b\fi (#2}%
\edef\qspairs@abort #1(?){\space\space}%
%
% we check if empty of single and if not pick up the first as Pivot:
\def\qspairs@b #1(#2)#3(#4){\ifx?#4\xintdothis\qspairs@empty\fi
                   \ifx!#4\xintdothis\qspairs@single\fi
                   \xintorthat \qspairs@separate {}{}{#2}(#4)}%
\def\qspairs@empty  #1(?){ }%
\edef\qspairs@single #1#2#3#4(?){\space\space(#3)}%
\def\qspairs@separate #1#2#3#4(#5)%
{%
    \ifx!#5\expandafter\qspairs@separate@done\fi
    \IfFirstPairIsGreaterTF {#5}{#3}%
          \qspairs@separate@appendtogreater
          \qspairs@separate@appendtosmaller {#5}{#1}{#2}{#3}%
}%
%
\def\qspairs@separate@appendtogreater #1#2{\qspairs@separate {#2 (#1)}}%
\def\qspairs@separate@appendtosmaller #1#2#3{\qspairs@separate {#2}{#3 (#1)}}%
%
\def\qspairs@separate@done\IfFirstPairIsGreaterTF #1#2%
    \qspairs@separate@appendtogreater
    \qspairs@separate@appendtosmaller #3#4#5#6(?)%
{%
    \expandafter\qspairs@f\expandafter
    {\romannumeral0\qspairs@b #4(!)(?)}{\qspairs@b #5(!)(?)}{ (#2)}%
}%
%
\def\qspairs@f #1#2#3{#2#3#1}%
%
% \catcode`! 12
% \catcode`? 12

\makeatother

\usepackage{geometry}

\begin{document}
\Large

\def\coordList{(5,5) (0,0) (2,5) (2,4) (-1, 3)}

Input: \coordList

After sorting: \QSpairs{\coordList}

Expected result: (-1, 3) (0,0) (2,4) (2,5) (5,5)

\bigskip

\def\coordList {
(735.578, -0.5)
(408.866, 7.2)
(513.653, 1)
(465.136, 17)
(323.362, 17)
(408.866, 3)
(408.866, 4)
(735.578, -0.1)
(408.866, 7.1)
(408.866, 5)
(886.204, 1.4)
(408.866, 2)
(649.711, 17)
(735.578, -1)
(886.204, 1.3)
(886.204, 1.2)
(254.715, 17)
(408.866, 6)
(886.204, 1)
(504.730, 17)
(504.730, -100)
(186.578, -2)
(735.578, -0.4)
(608.552, 0)
(408.866, 7.21)
(412.004, -1)
(886.204, 1.1)
(195.793, 17)
(408.866, 1)
(388.683, 17)
(140.974, -10)
(408.866, 7.201)
}

Input: \coordList

Output: \QSpairs{\coordList}

\end{document}

在此处输入图片描述----

更新处理维度坐标。

\documentclass{article}
\makeatletter
% This is based ond the "code 4" of
% https://tex.stackexchange.com/a/273084/4686,
% 
% modified to handle pairs (a, b) (c,d)(e, f) (g,h) etc...
% 
% acts expandably. Lexicographic order.

\makeatletter

% This variant assumes that all a's and b's are explicit dimension coordinates
% like 5pt or 20ex


\long\def\xintdothis #1#2\xintorthat #3{\fi #1}%
\let\xintorthat \@firstofone

\long\def\@thirdoffour  #1#2#3#4{#3}%
\long\def\@fourthoffour #1#2#3#4{#4}%

\def\IfFirstPairIsGreaterTF #1#2{\@IfFirstPairIsGreaterTF #1,#2,}%

% Th code handles also \dimen's like \ht\box0
% (but the output then can not be directly printed, it can
% only be used in contexts accepting \dimen's)
% Variant handling also things like \ht\box0
\def\@IfFirstPairIsGreaterTF #1,#2,#3,#4,{%
    \ifdim \dimexpr(#1)-(#3)=\z@
       \xintdothis{%
         \ifdim \dimexpr (#2)-(#4)>\z@\expandafter\@firstoftwo
         \else\expandafter\@secondoftwo\fi}\fi
    \ifdim \dimexpr(#1)-(#3)>\z@\expandafter\@thirdoffour
                      \else\expandafter\@fourthoffour\fi
    \xintorthat{}%
}%

% not needed for numerical inputs
% \catcode`! 3
% \catcode`? 3
% Here there is a very strange \romannumeral0\romannumeral0, this is
% due to some convoluted scheme to avoid double spaces or no spaces
% in between coordinate pairs. Trust me.
\def\QSpairs {\romannumeral0\romannumeral0\qspairs }%
% first we check if empty list (else \qsfull@finish will not find a comma)
\def\qspairs   #1{\expandafter\qspairs@a\romannumeral-`0#1(!)(?)}%
\def\qspairs@a #1(#2{\ifx!#2\expandafter\qspairs@abort\else
                        \expandafter\qspairs@b\fi (#2}%
\edef\qspairs@abort #1(?){\space\space}%
%
% we check if empty of single and if not pick up the first as Pivot:
\def\qspairs@b #1(#2)#3(#4){\ifx?#4\xintdothis\qspairs@empty\fi
                   \ifx!#4\xintdothis\qspairs@single\fi
                   \xintorthat \qspairs@separate {}{}{#2}(#4)}%
\def\qspairs@empty  #1(?){ }%
\edef\qspairs@single #1#2#3#4(?){\space\space(#3)}%
\def\qspairs@separate #1#2#3#4(#5)%
{%
    \ifx!#5\expandafter\qspairs@separate@done\fi
    \IfFirstPairIsGreaterTF {#5}{#3}%
          \qspairs@separate@appendtogreater
          \qspairs@separate@appendtosmaller {#5}{#1}{#2}{#3}%
}%
%
\def\qspairs@separate@appendtogreater #1#2{\qspairs@separate {#2 (#1)}}%
\def\qspairs@separate@appendtosmaller #1#2#3{\qspairs@separate {#2}{#3 (#1)}}%
%
\def\qspairs@separate@done\IfFirstPairIsGreaterTF #1#2%
    \qspairs@separate@appendtogreater
    \qspairs@separate@appendtosmaller #3#4#5#6(?)%
{%
    \expandafter\qspairs@f\expandafter
    {\romannumeral0\qspairs@b #4(!)(?)}{\qspairs@b #5(!)(?)}{ (#2)}%
}%
%
\def\qspairs@f #1#2#3{#2#3#1}%
%
% \catcode`! 12
% \catcode`? 12

\makeatother

\usepackage{geometry}


\begin{document}
\Large

\def\coordList{(5pt, 5pt) (0pt, 0pt) (2pt, 5pt) (2pt, 5bp) (2pt, 4pt) (2bp, 4pt) (-1pt, 3pt)}

Input: \coordList

After sorting: \QSpairs{\coordList}

\end{document}

在此处输入图片描述

答案2

此包forest实现了您可以使用的快速排序算法。该算法在手册的第 8.2 节(实现部分)中有描述。

要使用\forest@sort宏,必须定义两个宏:

  1. 对两个项目进行排序的宏:下面,\sortcoordinates

  2. TeX将一个项目复制(在s的意义上)到另一个项目上的宏\let:如下所示\letcoordinates

由于数组排序forest实际上是包内部的事情,因此没有很好的宏来设置和读取与之配套的数组,但这并不是一个主要障碍arrayjobx。(包multido用于遍历数组。)

下面的代码使用了 的实现细节arrayjobx,即42数组的项coordinates将存储在控制序列中coordinates42~

\documentclass{article}

\usepackage{arrayjobx}
\usepackage{multido}
\usepackage{forest}

\makeatletter
\def\letcoordinates#1#2{\csletcs{coordinates#1\string~}{coordinates#2\string~}}
\def\parsecoordinate(#1,#2)#3#4{%
  \def#3{#1 pt}%
  \def#4{#2 pt}%
}
\def\sortcoordinates#1#2{%
  \expandafter\expandafter\expandafter\parsecoordinate\csname coordinates#1\string~\endcsname\firstx\firsty
  \expandafter\expandafter\expandafter\parsecoordinate\csname coordinates#2\string~\endcsname\secondx\secondy
  \forest@sort@cmptwodimcs{firstx}{firsty}{secondx}{secondy}%
}
\makeatother

\begin{document}

\newarray\coordinates
\readarray{coordinates}{(5,5)&(0,0)&(2,5)&(2,4)&(-1, 3)}
\def\ncoordinates{5}
\newcounter{i}
Unsorted: \multido{\i=1+1}{\ncoordinates}{\coordinates(\i),}

\makeatletter
\forest@sort\sortcoordinates\letcoordinates\forest@sort@ascending{1}{\ncoordinates}%
\makeatother
Sorted: \multido{\i=1+1}{\ncoordinates}{\coordinates(\i),}

\end{document}

输出:

Unsorted: (5,5),(0,0),(2,5),(2,4),(-1, 3),
Sorted: (-1, 3),(0,0),(2,4),(2,5),(5,5),

相关内容