使用lambda.sty(为了娱乐)

使用lambda.sty(为了娱乐)

我正在尝试实现 (快速) 排序算法。以下是我能做的:

\documentclass[preview, border=7mm]{standalone}
\usepackage{pgffor}

% \push{xs}{\i} add \i in the array \xs 
\def\push#1#2{\expandafter\xdef\csname #1\endcsname{\expandafter\ifx\csname #1\endcsname\empty\else\csname #1\endcsname,\fi#2}}

\newcommand*{\qsort}[1]{%
  \edef\arr{#1}% the array to sort
  \foreach[count=\len] \i in \arr{}% take the length
  \ifnum\len>1\relax% if 0 or 1 elements in the array, nothing to sort
    \pgfmathsetmacro\midval{{\arr}[int(\len/2)]}% take the middle element (a random one is probably better) as pivot
    \let\xs\empty\let\xe\empty\let\xl\empty% \xs (s for small), \xe (e for equal), \xl (l for large)
    \foreach \i in \arr{%
      \ifdim \i pt < \midval pt%
        \push{xs}{\i}% if \i < pivot, push it in \xs
      \else%
        \ifdim \i pt = \midval pt%
          \push{xe}{\i}% if \i = pivot, push it in \xe
        \else%
          \push{xl}{\i}% if \i > pivot, push it in \xl
        \fi%
      \fi%
    }%
    \edef\nxs{\xs}\edef\nxe{\xe}\edef\nxl{\xl}% save the globals
    \ifx\nxs\empty\else{\qsort\nxs},\fi% cal qsort for \nxs if not empty
    \nxe% copy \nxe (in general containing only the pivot element)
    \ifx\nxl\empty\else,{\qsort\nxl}\fi% cal qsort for \nxl if not empty
  \else%
    \arr% 0 or 1 element array is already sorted
  \fi%
}
\begin{document}
  \let\temp\empty
  \foreach \i in {1,...,10}{\pgfmathparse{rnd}\push{temp}{\pgfmathresult}}
  \temp\newline
  when sorted becomes\newline
  \qsort\temp
\end{document}

在此处输入图片描述

我的主要问题是由于\foreach这个宏不可扩展。所以我无法制作\edef\sorted{\qsort\data}

所以我的问题是:如何定义一个可扩展(快速)排序,使用类似的数组{.1,10,.25}

精确:我把“快速”放在括号里,因为对我来说最重要的是可扩展。我的其他标准是:如果可能的话,要简短,如果可能的话,要快速。

答案1

本答案有四五 六四 套路

最后更新:更精简code 6,因为我更好地理解了我的不当使用\pdfescapestring,并且可以删除一些多余的额外功能(如果它们真的需要,那么这些额外功能无论如何都是不够的)。

最新更新:改进合并子程序,code 6至少可以2x提高速度。

  1. 第一个是直接改编在信特自发布以来的 .pdf 文档2013/10/29;在本节开头7.28 The Quick Sort algorithm illustrated有一个可扩展的 QuickSort 算法实现。由于它处理的是括号中的项,而不是逗号分隔的项,因此我需要添加从逗号分隔值到括号中的项的来回转换。此代码用于xinttools列表实用程序和xintfrac“实数”比较。

  2. 第二段代码直接处理逗号分隔的值。不再需要包xinttools。仍然使用 xintfrac,并且需要发布,1.2因为\xintdothis/\xintorthat早期版本中的使用名称_仅包含。它比代码版本 1 更高效,因为它进行的比较更少(代码 1 每次比较三次(!!!),代码 2 只比较两次)。

  3. 代码12仅出于情感原因而保留。第三个代码是代码 2 的变体,它只进行一次比较,因此我预计它会快两倍左右。但对于非常长的输入,它可能会因较长的标记列表而受到惩罚。用作xintfrac 1.2代码 2。

  4. 第四个代码与代码 3 完全相同,但xintfrac它假设数字可以与 进行比较\ifdim。因此,最后一个代码根本不需要任何包。不用说它也更快。但Dimension too large如果输入不合适,则容易出错。

    之前的更新是为了允许代码234接受空的优雅地输入(不是空值,只有空的全局输入)。

  5. 第五个也是最后\pdfstrcmp一个方法处理带有(在 下pdflatex) 或带有\strcmp(在 下)的字母输入 XeTeX。与此不同,此变体仅在\edef(或\message,等..) 中完全扩展,第一个标记的完全扩展是不够的,这与 1234提供的功能相反。输入中的空白或空白项将从输出中丢弃,初始空格将被删除,并且输出的每个逗号后都恰好有一个空格。项的最终空格(逗号前)保持原样(这意味着大多数情况下恰好一个空格标记,因为在特殊情况下需要创建连续多个空格标记的输入)。修改代码以使其像123并且4不要求那样运行并不难\edef,从而允许那些不希望在输出中展开的宏项。

    希望最终的编辑能够稍微提高代码速度,5因为它利用了第一遍之后空白消失的事实。

  6. 第六个是归并排序算法,自下而上的变体。为了便于扩展,我本可以采用自上而下的方式,但那样做需要(至少在简单的方法中)将项目计数分成两部分,然后进行递归。我选择了自下而上的方法,这在先验上导致了以可扩展方式实现的严重问题(**),这些问题通过我从与 Bruno Le Floch 的通信中学到的传播扩展技巧解决了:这个技巧就是使用原始的pdftex\pdfescapestring通过这种方式,我可以进行连续的传递,首先是二乘二,然后是四乘四,然后是八乘八,每次都留下已经处理过的材料。还有其他技术可用,但这会不可逆地填充部分内存TeX

    有一个不便之处,就是空格最终会被转义。因此,我在第一遍中抑制了它们,最终输出没有空格,与其他方法相反:它们可以在最后一遍中添加。我给出了用于对与 相当的十进制数进行排序的代码\ifdim。可以xintfrac用于任意精度的小数或分数。第一遍将应用于\xintRaw每个项目,将科学计数法转换为xintfrac A/B[N]符号,因为将从e获得 catcode ,并且在下一遍中,这将不会被识别(另一种方法是进行比较,其中 的 catcode无关紧要)。12\pdfescapestring\xintthefloatexpr...\relaxe

    此方法需要明确的输入,否则将会失败\a, \b, \c, \d

    (**)不是问题原则,但存在效率问题:可以将所有标记放在前面,特别是因为我们每次只需要抓取另外两个分隔符。为此,我们可以暂时将已经解析的材料放在新的两个东西之外,合并它们,然后在解析的内容末尾交换回来(如果我们想保持稳定的排序)。这意味着大量的标记改组,这正是我们在处理数千个标记时想要尽量减少的事情。

我还没有对第六个代码进行大量测试,但它似乎是最快的。 几乎比使用 1000 个十进制数的测试6x更快(上次更新带来的改进之后)。code 4abcd.wxyz

因为我的答案超出了最大大小,所以我删除了代码 1 和代码 2,为代码 6 腾出空间

全部快速排序变体每次都选择第一个元素作为枢轴,如果输入已经接近排序(或反向排序),则这很糟糕。

归并排序没有这样的问题。


代码 1(已删除)

引用


代码 2(已删除)

在此处输入图片描述


代码 3

\documentclass{article}
\usepackage{color}
\usepackage[straightquotes]{newtxtt}
%\usepackage{xinttools}
\usepackage{xintfrac}

% THE QUICK SORT ALGORITHM EXPANDABLY
% adapted from xint.pdf user manual 
% to act on comma separated values.

% this second variant does not do twice comparison tests, hence should
% be about 2x faster at least for not extremely long inputs.

\makeatletter
% not needed for numerical inputs
% \catcode`! 3
% \catcode`? 3
\def\QSfull {\romannumeral0\qsfull }%
% first we check if empty list (else \qsfull@finish will not find a comma)
\def\qsfull   #1{\expandafter\qsfull@a\romannumeral-`0#1,!,?}%
\def\qsfull@a #1{\ifx,#1\expandafter\qsfull@abort\else
                        \expandafter\qsfull@start\fi #1}%
\def\qsfull@abort #1?{ }%
\def\qsfull@start {\expandafter\qsfull@finish\romannumeral0\qsfull@b,}%
\def\qsfull@finish ,#1{ #1}%
%
%
% we check if empty of single and if not pick up the first as Pivot:
\def\qsfull@b ,#1#2,#3{\ifx?#3\xintdothis\qsfull@empty\fi
                   \ifx!#3\xintdothis\qsfull@single\fi
                   \xintorthat \qsfull@separate {}{}{#1#2}#3}%
\def\qsfull@empty  #1#2#3#4{ }%
\def\qsfull@single #1#2#3#4?{, #3}%
\def\qsfull@separate #1#2#3#4#5,%
{%
    \ifx!#4\expandafter\qsfull@separate@done\fi
    \romannumeral0\xintifgt {#4#5}{#3}%
          \qsfull@separate@appendtogreater
          \qsfull@separate@appendtosmaller {#4#5}{#1}{#2}{#3}%
}%
%
\def\qsfull@separate@appendtogreater #1#2{\qsfull@separate {#2,#1}}%
\def\qsfull@separate@appendtosmaller #1#2#3{\qsfull@separate {#2}{#3,#1}}%
%
\def\qsfull@separate@done\romannumeral0\xintifgt #1#2%
    \qsfull@separate@appendtogreater
    \qsfull@separate@appendtosmaller #3#4#5#6?%
{%
    \expandafter\qsfull@f\expandafter
    {\romannumeral0\qsfull@b #4,!,?}{\qsfull@b #5,!,?}{#2}%
}%
%
\def\qsfull@f #1#2#3{#2, #3#1}%
%
% \catcode`! 12
% \catcode`? 12
\makeatother

\begin{document}\thispagestyle{empty}\pagestyle{empty}
% EXAMPLE

\ttfamily

First example:

\edef\z {\QSfull {1.0, 0.5  ,0.3, 1.5  ,1.8,2.0,1.7, 0.4, 1.2, 1.4,
    1.3, 1.1, 0.7, 1.6, 0.6, 0.9, 0.8, 0.2  ,   0.1    ,1.9}}

1.0, 0.5  ,0.3, 1.5  ,1.8,2.0,1.7, 0.4, 1.2, 1.4,
    1.3, 1.1, 0.7, 1.6, 0.6, 0.9, 0.8, 0.2  ,   0.1    ,1.9

will give macro with meaning (notice that the output has the same extra spaces
after digits and before commas that the user may have left on input; this is a
feature, not a bug; on the other hand commas on output always are followed by a
space, again a feature).

\meaning\z

\bigskip
\clearpage

First example:
\begin{verbatim}
\def\a {3.123456789123456789}
\def\b {3.123456789123456788}
\def\c {3.123456789123456790}
\def\d {3.123456789123456787}
% \oodef expands exactly twice the replacement text.
\oodef\z {\QSfull { \a, \b, \c, \d}}
% The first item \a is prefixed by a space to avoid being expanded on input
\meaning\z\par
% with \fdef (faster than \oodef), we use lowercase \qsfull which inserts a
% space hence this will stop the \romannumeral-`0 from the \fdef and avoid
% expansion of the first item of output.
\fdef\w {\qsfull { \a, \b, \c, \d}}
\meaning\w\par
\end{verbatim}

\def\a {3.123456789123456789}
\def\b {3.123456789123456788}
\def\c {3.123456789123456790}
\def\d {3.123456789123456787}

\oodef\z {\QSfull { \a, \b, \c, \d}}% The \a is prefixed by a space not be
                                % expanded

\textcolor{blue}{\meaning\z}

\fdef\w {\qsfull { \a, \b, \c, \d}}
\textcolor{blue}{\meaning\w}

\bigskip

% Third example:
% \begin{verbatim}
% \edef\z {\QSfull {0.7, 0.5, 0.9, 0.1, 1.32, 1.31, 0.11, 0.12, 0.1}}

% \meaning\z
% \end{verbatim}
% \fdef\z {\QSfull {0.7, 0.5, 0.9, 0.1, 1.32, 1.31, 0.11, 0.12, 0.1}}

% \meaning\z

Second Example:
\begin{verbatim}
\edef\z{\QSfull {513653, 408866, 886204, 735578, 608552, 412004, 186578,
140974, 649711, 465136, 388683, 323362, 504730, 254715, 195793, 233621, 671299,
428170, 951423, 653603, 665197, 857787, 771700, 992045, 372838, 929992, 167911,
780076, 243286, 69869, 804743, 300412, 530086, 173893, 900738, 493815, 9980,
448301, 824577, 514010, 522537, 919250, 986196, 774531, 240895, 956406, 685109,
604079, 736896, 111898, 682533, 542445, 674548, 889917, 896843, 449541, 639348,
747223, 832750, 182026, 460351, 337944, 354216, 528491, 177447, 223522, 438099,
906754, 992992, 512190, 369322, 613899, 897603, 288589, 383803, 679017, 97174,
320213, 59265, 64112, 769517, 138982, 902828, 426525, 951653, 848634, 786758,
121220, 287689, 165161, 885263, 597976, 261723, 683603, 864299, 57401, 530568,
662834, 269801, 986180}}
\meaning\z
\end{verbatim}

\edef\z{\QSfull {513653, 408866, 886204, 735578, 608552, 412004, 186578,
140974, 649711, 465136, 388683, 323362, 504730, 254715, 195793, 233621, 671299,
428170, 951423, 653603, 665197, 857787, 771700, 992045, 372838, 929992, 167911,
780076, 243286, 69869, 804743, 300412, 530086, 173893, 900738, 493815, 9980,
448301, 824577, 514010, 522537, 919250, 986196, 774531, 240895, 956406, 685109,
604079, 736896, 111898, 682533, 542445, 674548, 889917, 896843, 449541, 639348,
747223, 832750, 182026, 460351, 337944, 354216, 528491, 177447, 223522, 438099,
906754, 992992, 512190, 369322, 613899, 897603, 288589, 383803, 679017, 97174,
320213, 59265, 64112, 769517, 138982, 902828, 426525, 951653, 848634, 786758,
121220, 287689, 165161, 885263, 597976, 261723, 683603, 864299, 57401, 530568,
662834, 269801, 986180}}
\textcolor{blue}{\meaning\z}


+++\QSfull{}+++

\end{document}

引用


代码 4. 不需要包,但仅限于可以比较的十进制数\ifdim

\documentclass{article}
\usepackage{color}
\usepackage[straightquotes]{newtxtt}
\usepackage{xintkernel}

% THE QUICK SORT ALGORITHM EXPANDABLY
% adapted from xint.pdf user manual 
% to act on comma separated values.

% this variant uses no package whatsoever
% inputs must be comparable via \ifdim

\makeatletter
% defined here to avoid having to load xintkernel package
\long\def\xintdothis #1#2\xintorthat #3{\fi #1}%
\let\xintorthat \@firstofone
%
\makeatletter
% not needed for numerical inputs
% \catcode`! 3
% \catcode`? 3
\def\QSfull {\romannumeral0\qsfull }%
% first we check if empty list (else \qsfull@finish will not find a comma)
\def\qsfull   #1{\expandafter\qsfull@a\romannumeral-`0#1,!,?}%
\def\qsfull@a #1{\ifx,#1\expandafter\qsfull@abort\else
                        \expandafter\qsfull@start\fi #1}%
\def\qsfull@abort #1?{ }%
\def\qsfull@start {\expandafter\qsfull@finish\romannumeral0\qsfull@b,}%
\def\qsfull@finish ,#1{ #1}%
%
% we check if empty of single and if not pick up the first as Pivot:
\def\qsfull@b ,#1#2,#3{\ifx?#3\xintdothis\qsfull@empty\fi
                       \ifx!#3\xintdothis\qsfull@single\fi
                       \xintorthat \qsfull@separate {}{}{#1#2}#3}%
\def\qsfull@empty    #1#2#3#4{ }%
\def\qsfull@single   #1#2#3#4?{, #3}%
\def\qsfull@separate #1#2#3#4#5,%
{%
    \ifx!#4\expandafter\qsfull@separate@done\fi
    \ifdim #4#5pt>#3pt
         \expandafter\qsfull@separate@appendtogreater
    \else\expandafter\qsfull@separate@appendtosmaller
    \fi
          {#4#5}{#1}{#2}{#3}%
}%
%
\def\qsfull@separate@appendtogreater #1#2{\qsfull@separate {#2,#1}}%
\def\qsfull@separate@appendtosmaller #1#2#3{\qsfull@separate {#2}{#3,#1}}%
%
\def\qsfull@separate@done\ifdim #1%
    \expandafter\qsfull@separate@appendtogreater
    \else\expandafter\qsfull@separate@appendtosmaller\fi #2#3#4#5?%
{%
    \expandafter\qsfull@f\expandafter
    {\romannumeral0\qsfull@b #3,!,?}{\qsfull@b #4,!,?}{#5}%
}%
%
\def\qsfull@f #1#2#3{#2, #3#1}%
%
% \catcode`! 12
% \catcode`? 12
\makeatother


\begin{document}\thispagestyle{empty}\pagestyle{empty}
% EXAMPLE

\ttfamily

First example:

\edef\z {\QSfull {1.0, 0.5  ,0.3, 1.5  ,1.8,2.0,1.7, 0.4, 1.2, 1.4,
    1.3, 1.1, 0.7, 1.6, 0.6, 0.9, 0.8, 0.2  ,   0.1    ,1.9}}

1.0, 0.5  ,0.3, 1.5  ,1.8,2.0,1.7, 0.4, 1.2, 1.4,
    1.3, 1.1, 0.7, 1.6, 0.6, 0.9, 0.8, 0.2  ,   0.1    ,1.9

will give macro with meaning (notice that the output has the same extra spaces
after digits and before commas that the user may have left on input; this is a
feature, not a bug; on the other hand commas on output always are followed by a
space, again a feature).

\meaning\z

\def\a{3.14}
\def\b{3.11}
\def\c{3.2}
\def\d{3.09}
\expandafter\def\expandafter\z\expandafter{\romannumeral0\qsfull { \a, \b, \c, \d}}

\meaning\z


\bigskip
\clearpage
\bigskip

Example:
\begin{verbatim}
\oodef\z{\QSfull {513.653, 408.866, 886.204, 735.578, 608.552, 412.004,
186.578, 140.974, 649.711, 465.136, 388.683, 323.362, 504.730, 254.715,
195.793, 233.621, 671.299, 428.170, 951.423, 653.603, 665.197, 857.787,
771.700, 992.045, 372.838, 929.992, 167.911, 780.076, 243.286, 698.69,
804.743, 300.412, 530.086, 173.893, 900.738, 493.815, 9.980, 448.301, 824.577,
514.010, 522.537, 919.250, 986.196, 774.531, 240.895, 956.406, 685.109,
604.079, 736.896, 111.898, 682.533, 542.445, 674.548, 889.917, 896.843,
449.541, 639.348, 747.223, 832.750, 182.026, 460.351, 337.944, 354.216,
528.491, 177.447, 223.522, 438.099, 906.754, 992.992, 512.190, 369.322,
613.899, 897.603, 288.589, 383.803, 679.017, 97.174, 320.213, 592.65, 641.12,
769.517, 138.982, 902.828, 426.525, 951.653, 848.634, 786.758, 121.220,
287.689, 165.161, 885.263, 597.976, 261.723, 683.603, 864.299, 574.01,
530.568, 662.834, 269.801, 986.180}}
\meaning\z
\end{verbatim}

\oodef\z{\QSfull {513.653, 408.866, 886.204, 735.578, 608.552, 412.004,
186.578, 140.974, 649.711, 465.136, 388.683, 323.362, 504.730, 254.715,
195.793, 233.621, 671.299, 428.170, 951.423, 653.603, 665.197, 857.787,
771.700, 992.045, 372.838, 929.992, 167.911, 780.076, 243.286, 698.69,
804.743, 300.412, 530.086, 173.893, 900.738, 493.815, 9.980, 448.301, 824.577,
514.010, 522.537, 919.250, 986.196, 774.531, 240.895, 956.406, 685.109,
604.079, 736.896, 111.898, 682.533, 542.445, 674.548, 889.917, 896.843,
449.541, 639.348, 747.223, 832.750, 182.026, 460.351, 337.944, 354.216,
528.491, 177.447, 223.522, 438.099, 906.754, 992.992, 512.190, 369.322,
613.899, 897.603, 288.589, 383.803, 679.017, 97.174, 320.213, 592.65, 641.12,
769.517, 138.982, 902.828, 426.525, 951.653, 848.634, 786.758, 121.220,
287.689, 165.161, 885.263, 597.976, 261.723, 683.603, 864.299, 574.01,
530.568, 662.834, 269.801, 986.180}}
\textcolor{blue}{\meaning\z}

+++\QSfull{}+++

\end{document}

引用


代码 5。同样不需要包,此变体使用\pdfstrcmpXeTeX的进行字母比较\strcmp。这需要一个\edef,但可以将其更改为像其他的一样。输入中的空白项将被吞噬,不会进入输出。输出中每个逗号后面都有一个空格,位于项之前。

% COMPILE WITH XETEX (UTF8 file)

\documentclass{article}
\usepackage{color}
\usepackage[straightquotes]{newtxtt}
%\usepackage[french]{babel}% I tried to see if it impacted alphabetical
% sorting of é è etc... (no, even with french as class option)

% VARIANT FOR ALPHABETICAL INPUTS

% uses \pdfstrcmp from pdftex or \strcmp from xetex
% this variant only expands completely in an \edef 
% (full expansion of first token not enough).

% blank items are accepted and _discarded_.

% empty list (or list of only blank items) is also accepted and produces empty
% output.

\ifdefined\XeTeXinterchartoks
   \let\pdfstrcmp\strcmp
\else
   \usepackage[utf8]{inputenc}% for PDFLaTeX
\fi

\makeatletter
% defined here to avoid having to load xintkernel package
\long\def\xintdothis #1#2\xintorthat #3{\fi #1}%
\let\xintorthat \@firstofone
%
% use some (improbable) tokens as delimiters
\catcode`! 3
\catcode`? 3
\catcode`; 3
% first we check if empty list (else \qsfull@finish will not find a comma)

% Needs an \edef for expansion
% First we apply f-expansion to the argument to allow it to be a macro.
%
\def\QSfull #1{\expandafter\qsfull@a\romannumeral-`0#1,!,?}%
%
% first check if input has only blanks, or is empty
\def\qsfull@a #1{\ifx,#1\xintdothis\qsfull@a\fi
                 \ifx!#1\xintdothis\qsfull@abort\fi
                 \xintorthat{\qsfull@start #1}}%
\def\qsfull@abort #1?{}%
%
\def\qsfull@start {\expandafter\qsfull@finish\romannumeral0\qsfull@b,}%
\def\qsfull@finish ,#1{#1}% remove initial ,<space>

% We don't need to filter out case of blank first element, because it can
% never happen. The blanks are removed in the first pass and this first pass
% uses necessarily a non-blank pivot.

% We filter out case of empty or singleton list.

% The first item here can NOT be a blank, which would cause #1 to pick up a
% comma.

\def\qsfull@b ,#1#2,#3{\ifx?#3\xintdothis\qsfull@emptylist\fi
                       \ifx!#3\xintdothis\qsfull@singleton\fi
                       \xintorthat \qsfull@separate@a {}{}#1#2;#3}%
\def\qsfull@emptylist    #1?{}%
\def\qsfull@singleton    #1#2#3;!,?{, #3}%
%
\def\qsfull@separate@a  #1#2#3;#4#5,%
% first pass, remove blanks in passing.
% no need to be extra efficient for that.
{%
    \ifx,#4\expandafter\qsfull@valueisblank\fi
    \ifx!#4\expandafter\qsfull@separate@done\fi
    \if1\pdfstrcmp{#4#5}{#3}%
         \expandafter\qsfull@separate@a@appendtogreater
    \else\expandafter\qsfull@separate@a@appendtosmaller
    \fi
          #4#5?{#1}{#2}#3;%
}%
\def\qsfull@valueisblank \ifx#1\fi,#2?#3#4#5;{\qsfull@separate@a {#3}{#4}#5;#2,}%
\def\qsfull@separate@a@appendtogreater #1?#2{\qsfull@separate@a {#2, #1}}%
%
\def\qsfull@separate@a@appendtosmaller #1?#2#3{\qsfull@separate@a {#2}{#3, #1}}%
%
\def\qsfull@separate@done\if#1\fi #2?#3#4#5;?%
{%
    \qsfull@c #4,!,?, #5\qsfull@c #3,!,?%
}%
% Now that the first pass is done, there are no more blank items.
% In particular here.
\def\qsfull@c ,#1#2,#3{\ifx?#3\xintdothis\qsfull@emptylist\fi
                       \ifx!#3\xintdothis\qsfull@singleton\fi
                       \xintorthat \qsfull@separate {}{}#1#2;#3}%
%
\def\qsfull@separate  #1#2#3;#4#5,% blanks have already been filtered out.
{%
    \ifx!#4\expandafter\qsfull@separate@done\fi
    \if1\pdfstrcmp{#4#5}{#3}%
         \expandafter\qsfull@separate@a@appendtogreater
    \else\expandafter\qsfull@separate@a@appendtosmaller
    \fi
          #4#5?{#1}{#2}#3;%
}%
\def\qsfull@separate@appendtogreater #1?#2{\qsfull@separate {#2, #1}}%
\def\qsfull@separate@appendtosmaller #1?#2#3{\qsfull@separate {#2}{#3, #1}}%
%
%
\catcode`! 12
\catcode`? 12
\catcode`; 12
\makeatother


\begin{document}\thispagestyle{empty}\pagestyle{empty}
% EXAMPLE

\ttfamily

Example:

\edef\z{\QSfull{b,a}}
+++\z+++

\def\w {(notice , that , the , output , has , the , extra , space tokens,
  before , commas , from the input , , , , , , , , except, that, blank items,
  from the inputs, are, discarded, from the output, !!!!, this, is, a,
  feature, or, a, bug, ?,,,,,,,,,,, a,feature,naturally,., And,commas, in,
  output, always, are, followed, by, a, space, (, a,feature, which, can, be,
  easily, modified, in the code, ), , , , , , , , }


\edef\z{\QSfull\w}

\w\textcolor{green}{ENDOFINPUT}

becomes

\textcolor{blue}{\meaning\z}\textcolor{green}{ENDOFOUTPUT}


+++\QSfull{}+++

More reasonable example:

\def\w {Demain, dès l'aube, à l'heure où blanchit la campagne,
Je partirai. Vois-tu, je sais que tu m'attends.
J'irai par la forêt, j'irai par la montagne.
Je ne puis demeurer loin de toi plus longtemps.}

\edef\z{\QSfull\w}

\w

becomes

\textcolor{blue}{\meaning\z}

+++\QSfull{,}+++

Last example

\def\w {
    déraison,
    imprudence,
    erreur,
    fantaisie,
    frénésie,
    fièvre,
    écart,
    bêtise,
    emportement,
    vertige,
    imbécillité,
    chimère,
    connerie,
    passion,
    bizarrerie,
    loufoquerie,
    excès,
    caprice,
    marotte,
    égarement,
    hallucination,
    sottise,
    dérèglement,
    fêlure,
    extravagance,
    divagation,
    mode,
    fredaine,
    maladie mentale,
    névrose,
    dérangement,
    manie,
    imagination,
    insanité,
    humeur,
    lubie,
    aveuglement,
    trouble,
    aliénation,
    rage,
    idiotie,
    aberration,
    crétinisme,
    toquade,
    témérité,
    monstruosité,
    démence,
    dissipation,
    délire,
    psychose,
    saugrenuité,
    affolement,
    dépression,
    déséquilibre,
    frasque,
    obstination,
    absurdité}


\edef\z{\QSfull\w}

\w

becomes

\textcolor{blue}{\meaning\z}

+++\QSfull{,,}+++

\enlargethispage{\baselineskip}
I am surprised that ``é'' comes after all lowercase letters.
\end{document}

引用


代码 6:使用可扩展编程的高级技术进行自下而上的归并排序。需要pdftex(或至少相当于\pdfescapestring)。此代码适用于 接受的十进制数\ifdim。如果需要,可以扩展到其他比较例程。

\documentclass{article}
\usepackage{color}
\usepackage[straightquotes]{newtxtt}
\usepackage{xinttools}% NOT used by the code, only for inserting spaces after
                      % commas in ouput of \MergeSort in the example

\makeatletter
\def\MergeSort {\romannumeral0\mergesort }%
% The routine expects to manipulate explicit decimal numbers as acceptable
% by \ifdim tests.

% there are slightly faster techniques than the use of \ifx, \if conditionals
% in the macros below, but for the sake of legibility of the code, I wrote
% this draft using them nevertheless.

% WE EXPAND LIST ARGUMENT AND CHECK IF EMPTY
\def\mergesort #1{\expandafter\msort@a\romannumeral-`0#1,!,?,}%
\def\msort@a   #1{\ifx,#1\expandafter\msort@abort\else
                         \expandafter\msort@start\fi #1}%
\def\msort@abort #1?,{ }%
%
% FIRST BOTTOM-UP PASS, WE TAKE THIS OPPORTUNITY TO INSERT DELIMITERS
% and using \pdfescapestring trick to propagate expansion
% 
\def\msort@start {\expandafter\msort@c\pdfescapestring\bgroup\msort@b}%
\def\msort@b #1#2,#3#4,%
{%
    \ifx?#3\expandafter\msort@be\fi
    \ifx!#3\expandafter\msort@bs\fi
    \ifdim #1#2\p@>#3#4\p@
         \expandafter\@firstoftwo
    \else\expandafter\@secondoftwo
    \fi
    {#3#4,#1#2}{#1#2,#3#4},!,;\msort@b 
}%
\def\msort@be #1\msort@b {\iffalse{\fi}!;?;}%
\def\msort@bs \ifdim #1\p@#2\fi #3?,{#1\iffalse{\fi},!,;!;?;}%

% MERGING SUB-ROUTINE TO BE USED NEXT
% (improved: if first item of second bigger, keep it as long as necessary)
\def\msort@merge #1#2,#3;#4#5,%
{%
    \if!#1\expandafter\msort@merge@ea\fi
    \if!#4\expandafter\msort@merge@eb\fi
    \ifdim #1#2\p@>#4#5\p@ #4#5,%
       \expandafter\msort@merge
    \else\msort@merge@a #4#5%
    \fi #1#2,#3;%
}%
%
\def\msort@merge@ea #1\msort@merge@a #2\fi !,;{#2,}% possibly #2=!
\def\msort@merge@eb #1\fi #2;{#2}%
%
\def\msort@merge@a #1\fi #2,{\fi #2,\msort@merge@b #1,}%
\def\msort@merge@b #1,#2#3,%
{%
    \if!#2\expandafter\msort@merge@ea\fi
    \ifdim #2#3\p@>#1\p@ #1,%
         \expandafter\msort@merge
    \else\msort@merge@a #1%
    \fi #2#3,%
}%

% DO WE HAVE ONLY ONE LIST LEFT ? 
%    YES -> DONE
%     NO -> MERGE TWO AND KEEP GOING
\def\msort@c #1;#2#3;%
{%
    \if!#2\expandafter\msort@finish\fi
    \expandafter\msort@c
    \pdfescapestring\bgroup\msort@merge #1;#2#3;\msort@d 
}%

% KEEP ON FETCHING TWO BY TWO UNTIL HITTING FINAL ! OR ?
\def\msort@d #1#2;#3#4;%
{%
    \if?#3\expandafter\msort@de\fi
    \if!#3\expandafter\msort@ds\fi
    \msort@merge #1#2;#3#4;\msort@d 
}%
\def\msort@de #1\msort@d {\iffalse{\fi}!;?;}%
\def\msort@ds\msort@merge #1;#2?;{#1\iffalse{\fi};!;?;}%

% THIS IS FINAL CLEAN UP.
% THIS IS WHERE SPACES COULD GET REINSERTED AFTER COMMAS IF WANTED.
\def\msort@finish #1\msort@merge #2,!#3?;{ #2}%

\makeatother

\begin{document}\thispagestyle{empty}

+++\MergeSort {}+++

+++\MergeSort {1.1}+++

\MergeSort {1.2, 1.1, 1.3, 1.5, 0.9, 0.5}

\def\mylist{513.653, 408.866, 886.204, 735.578, 608.552, 412.004,
186.578, 140.974, 649.711, 465.136, 388.683, 323.362, 504.730, 254.715,
195.793, 233.621, 671.299, 428.170, 951.423, 653.603, 665.197, 857.787,
771.700, 992.045, 372.838, 929.992, 167.911, 780.076, 243.286, 698.69,
804.743, 300.412, 530.086, 173.893, 900.738, 493.815, 9.980, 448.301, 824.577,
514.010, 522.537, 919.250, 986.196, 774.531, 240.895, 956.406, 685.109,
604.079, 736.896, 111.898, 682.533, 542.445, 674.548, 889.917, 896.843,
449.541, 639.348, 747.223, 832.750, 182.026, 460.351, 337.944, 354.216,
528.491, 177.447, 223.522, 438.099, 906.754, 992.992, 512.190, 369.322,
613.899, 897.603, 288.589, 383.803, 679.017, 97.174, 320.213, 592.65, 641.12,
769.517, 138.982, 902.828, 426.525, 951.653, 848.634, 786.758, 121.220,
287.689, 165.161, 885.263, 597.976, 261.723, 683.603, 864.299, 574.01,
530.568, 662.834, 269.801, 986.180}

\expandafter\def\expandafter\z\expandafter{\romannumeral0\mergesort\mylist}

\show\z

With extra inserted spaces after commas before printing:

\textcolor{blue}{\xintListWithSep{, }{\xintCSVtoList{\MergeSort\mylist}}}

\end{document}

引用

答案2

使用以下简短演示l3sort

\documentclass{article}
\usepackage{expl3,l3sort,xparse}
\ExplSyntaxOn
\cs_set:Npn \quicksort_fp:nnTF #1#2
  { \fp_compare:nNnTF {#2} > {#1} }
\DeclareExpandableDocumentCommand \quicksort { m }
  { \tl_sort:nN {#1} \quicksort_fp:nnTF }
\ExplSyntaxOff
\edef\demo
  {%
    \quicksort
      {%
        {1.0}{0.5}{0.3}{1.5}{1.8}{2.0}{1.7}{0.4}{1.2}{1.4}
        {1.3}{1.1}{0.7}{1.6}{0.6}{0.9}{0.8}{0.2}{0.1}{1.9}
     }%
 }
\show\demo

目前,此处的代码还处于实验阶段,特别是只有通用tl排序可扩展。如果需要,可以将其扩展到逗号列表或类似列表。

答案3

该算法使用第一个、中间和最后一个元素的中位数作为枢轴。我试图尽量减少文本和数字之间的转换,假设 比\csname更快\strip@pt。(考虑到所有\expandafter需要 ,我想知道这是否真的如此。)

第一步是解析以逗号分隔的字符串存储的数组(换句话说,tikz 数组),并将其转换为\csname形式为\QSarray1, \QSarray2, ... 的全局数组。这就是递归宏所使用的\quicksort{low}{high}。最后,将排序后的数组转换回逗号分隔的字符串。

由于前导空白似乎不会影响排序,因此删除它们的最佳位置是在输出阶段(使用\ignorespaces)。但这可能会对其他处理产生不利影响。

我突然想到,会有很多 3 个或更少的块需要排序,所以我针对这些情况简化了代码。我发现,虽然通常只有少数条目等于基准,但在某些情况下,从进一步排序中删除基准是唯一的进展。

\documentclass{article}

\let\END=\eof% almost anything will do

\def\qsort#1#2% #1 = input array of decimal numbers, #2 = macro name for sorted output array
{\bgroup% assign resisters for all routines
  \countdef\QStotal=1
  \countdef\QSindex=2
  \countdef\QSlow=3
  \countdef\QShigh=4
  \countdef\QSmiddle=5
%
  \dimendef\QSpivot=0
  \dimendef\QStest=1
  \dimendef\QSalt=2
% parse comma delimited data and store in QSarray1, QSarray2, ..
  \QStotal=0
  \expandafter\parseCSV\detokenize #1,\END,\relax

% sort QSarray
  \quicksort{1}{\the\QStotal}%
% create output array (string)
  \QSindex=1
  \edef\QStemp{\ignorespaces\csname QSarray1\endcsname}%
  \global\expandafter\let\csname QSarray1\endcsname=\relax
  \loop\advance\QSindex by 1
    \edef\QStemp{\QStemp,\ignorespaces\csname QSarray\the\QSindex\endcsname}%
    \global\expandafter\let\csname QSarray\the\QSindex\endcsname=\relax% clear space
    \ifnum\QSindex<\QStotal \repeat
  \xdef#2{\QStemp}%
\egroup}

\def\parseCSV #1,{\ifx\END#1\relax\else
  \advance\QStotal by 1
  \expandafter\xdef\csname QSarray\the\QStotal\endcsname{#1}%
  \expandafter\parseCSV\fi}

\newcommand{\quicksort}[2]% #1 = low index, %2 = high index
{\bgroup% use local variables
  \edef\plow{#1}
  \edef\phigh{#2}
  \QSindex=\numexpr \phigh-\plow+1\relax
  \ifnum\QSindex>1
    \ifnum\QSindex=2 \QSortTwo
    \else
      \ifnum\QSindex=3 \QSortThree
      \else \QSortN
      \fi
    \fi
  \fi
\egroup}

\newcommand{\QSortN}{% use median of first, last, and middle for pivot
  \QSmiddle=\numexpr \plow+\QSindex/2\relax
  \edef\pmiddle{\the\QSmiddle}
  \QStest=\csname QSarray\plow\endcsname pt
  \QSpivot=\csname QSarray\pmiddle\endcsname pt
  \QSalt=\csname QSarray\phigh\endcsname pt
  \ifdim\QStest<\QSpivot
    \ifdim\QSpivot<\QSalt %\QSpivot=\QSpivot
    \else
      \ifdim\QStest<\QSalt \QSpivot=\QSalt
        \let\pmiddle=\phigh
      \else \QSpivot=\QStest
        \let\pmiddle=\plow
      \fi
    \fi
  \else
    \ifdim\QStest<\QSalt \QSpivot=\QStest
      \let\pmiddle=\plow
    \else
      \ifdim\QSpivot<\QSalt \QSpivot=\QSalt
      \let\pmiddle=\phigh
      \fi
    \fi
  \fi
  \expandafter\let\expandafter\pmiddle\csname QSarray\pmiddle\endcsname
% separate into \QSlow1, \QSlow2, ... and \QShigh1, \QShigh2, ...
  \QSlow=0
  \QSmiddle=0
  \QShigh=0
  \QSindex=\plow\relax
  \loop
    \expandafter\let\expandafter\temp\csname QSarray\the\QSindex\endcsname
    \QStest=\temp pt
    \ifdim\QStest<\QSpivot
      \advance\QSlow by 1
      \expandafter\let\csname low\the\QSlow\endcsname=\temp
    \else
      \ifdim\QStest>\QSpivot
        \advance\QShigh by 1
        \expandafter\let\csname high\the\QShigh\endcsname=\temp
      \else
        \advance\QSmiddle by 1
      \fi
    \fi
    \advance\QSindex by 1
  \ifnum\QSindex>\phigh\relax\else\repeat
% put low and high values (pointers) back into QSarray
  \QSindex=\plow
  \loop\ifnum\QSlow>0
    \expandafter\let\expandafter\temp\csname low\the\QSlow\endcsname
    \global\expandafter\let\csname QSarray\the\QSindex\endcsname=\temp
    \advance\QSindex by 1
    \advance\QSlow by -1
  \repeat
  \QSlow=\numexpr \QSindex-1\relax
  \edef\highestlow{\the\QSlow}
  \loop\ifnum\QSmiddle>0
    \global\expandafter\let\csname QSarray\the\QSindex\endcsname=\pmiddle
    \advance\QSindex by 1
    \advance\QSmiddle by -1
  \repeat
  \edef\lowesthigh{\the\QSindex}
  \loop\ifnum\QShigh>0
    \expandafter\let\expandafter\temp\csname high\the\QShigh\endcsname
    \global\expandafter\let\csname QSarray\the\QSindex\endcsname=\temp
    \advance\QSindex by 1
    \advance\QShigh by -1
  \repeat
% sort low and high values separately
  \quicksort{\plow}{\highestlow}%
  \quicksort{\lowesthigh}{\phigh}%
}

\newcommand{\QSortTwo}{% called frequently
  \expandafter\let\expandafter\tempa\csname QSarray\plow\endcsname
  \expandafter\let\expandafter\tempb\csname QSarray\phigh\endcsname
  \QStest=\tempa pt
  \QSalt=\tempb pt
  \ifdim\QStest>\QSalt
    \global\expandafter\let\csname QSarray\plow\endcsname=\tempb
    \global\expandafter\let\csname QSarray\phigh\endcsname=\tempa
  \fi}

\newcommand{\QSortThree}{% three way swap
  \QSmiddle=\numexpr \plow+1\relax
  \edef\pmiddle{\the\QSmiddle}
  \expandafter\let\expandafter\tempa\csname QSarray\plow\endcsname
  \expandafter\let\expandafter\tempb\csname QSarray\pmiddle\endcsname
  \expandafter\let\expandafter\tempc\csname QSarray\phigh\endcsname
  \QStest=\tempa pt
  \QSpivot=\tempb pt
  \QSalt=\tempc pt
  \ifdim\QStest<\QSpivot
    \ifdim\QSpivot<\QSalt% test < pivot < alt
    \else
      \global\expandafter\let\csname QSarray\phigh\endcsname=\tempb
      \ifdim\QStest<\QSalt% test < alt < pivot
        \global\expandafter\let\csname QSarray\pmiddle\endcsname=\tempc
      \else% alt < test < pivot
        \global\expandafter\let\csname QSarray\plow\endcsname=\tempc
        \global\expandafter\let\csname QSarray\pmiddle\endcsname=\tempa
      \fi
    \fi
  \else
    \ifdim\QSpivot<\QSalt
      \global\expandafter\let\csname QSarray\plow\endcsname=\tempb
      \ifdim\QStest<\QSalt% pivot < test < alt
        \global\expandafter\let\csname QSarray\pmiddle\endcsname=\tempa
      \else% pivot < alt < test
        \global\expandafter\let\csname QSarray\pmiddle\endcsname=\tempc
        \global\expandafter\let\csname QSarray\phigh\endcsname=\tempa
      \fi
    \else% alt < pivot < test
      \global\expandafter\let\csname QSarray\plow\endcsname=\tempc
      \global\expandafter\let\csname QSarray\phigh\endcsname=\tempa
    \fi
  \fi}

\begin{document}

\edef\test{{1,10,2,9,3,8,4,7,5,6}}% tikz array

\qsort{\test}{\result}
\result

\end{document}

就我个人而言,我不太关心如何让事情变得可扩展。从那时起,我能找到的最好的东西是:

\def\bar{\edef\test{TEST}\test}
\edef\foo{\noexpand\bar}
\foo
(\meaning\foo)

\def\foo{\bar}
\foo
(\meaning\foo)

无论如何,为了从\bgroup...中获取数据\egroup,您无论如何都需要一个全局宏,那么为什么不将其用于输出呢?

另一方面,我对快速排序程序很好奇,我以前从未实现过。然而,它并不是最快的排序程序。自平衡二叉树对所有数据集都执行 N log N。

\documentclass{article}

\let\END=\eof% almost anything will do

% returns sorted data in \QSresult
% global arrays: \QSdata, \QSlow, \QShigh, \QSmiddle, \QSlowindex and \QShighindex

\newcommand*{\qsort}[1]% #1 = input array of decimal numbers
{\bgroup% assign resisters for all routines
  \countdef\QStotal=1
  \countdef\QSindex=2
  \countdef\QSlow=3
  \countdef\QShigh=4
  \countdef\QSmiddle=5
%
  \dimendef\QSvalue=0
  \dimendef\QStest=1
% parse comma delimited data and store in QSarray1, QSarray2, ..
  \QStotal=0
  \expandafter\parseCSV\detokenize #1,\END,\relax
% after this point name conflicts no longer matter
% build tree
  \QSindex=0
  \QScreate
  \edef\start{1}%
  \loop\ifnum\QSindex<\QStotal
    \QScreate
    \QStest=\csname QSdata\theindex\endcsname pt\relax
    \QSpivot{\start}%
    \let\start=\QSresult
  \repeat
% create output array (string)
  \QSindex=0
  \QSsearch{\start}%
  \edef\temp{\QSresult}%
  \advance\QSindex by 1
  \loop
    \QSsearch{\start}%
    \edef\temp{\temp,\QSresult}%
    \advance\QSindex by 1
  \ifnum\QSindex<\QStotal \repeat
  \global\let\QSresult=\temp%
% clean up
  \QSindex=0
  \loop\QSdestroy
  \ifnum\QSindex<\QStotal \repeat
\egroup}

\def\parseCSV #1,{\ifx\END#1\relax\else
  \advance\QStotal by 1
  \expandafter\xdef\csname QSdata\the\QStotal\endcsname{#1}%
  \expandafter\parseCSV\fi}

\newcommand*{\QScreate}{% initialize data structure
  \advance\QSindex by 1
  \edef\theindex{\the\QSindex}%
  \expandafter\xdef\csname QSlow\theindex\endcsname{0}%
  \expandafter\xdef\csname QShigh\theindex\endcsname{0}%
  \expandafter\xdef\csname QSmiddle\theindex\endcsname{1}%
  \expandafter\xdef\csname QSlowindex\theindex\endcsname{}%
  \expandafter\xdef\csname QShighindex\theindex\endcsname{}%
}
\newcommand*{\QSdestroy}{% reset everything to \relax
  \advance\QSindex by 1
  \edef\theindex{\the\QSindex}%
  \global\expandafter\let\csname QSdata\theindex\endcsname=\relax
  \global\expandafter\let\csname QSlow\theindex\endcsname=\relax
  \global\expandafter\let\csname QShigh\theindex\endcsname=\relax
  \global\expandafter\let\csname QSmiddle\theindex\endcsname=\relax
  \global\expandafter\let\csname QSlowindex\theindex\endcsname=\relax
  \global\expandafter\let\csname QShighindex\theindex\endcsname=\relax
}
\newcommand*{\QSnopivot}[1]% #1 = tree index
{\bgroup% use local values
  \edef\start{#1}%
  \QSvalue=\csname QSdata\start\endcsname pt\relax
  \ifdim\QStest<\QSvalue
    \QSlow=\csname QSlow\start\endcsname\relax
    \ifnum\QSlow=0\relax
      \expandafter\xdef\csname QSlowindex\start\endcsname{\theindex}%
      \expandafter\xdef\csname QSlow\start\endcsname{1}%
    \else
      \advance\QSlow by 1
      \expandafter\xdef\csname QSlow\start\endcsname{\the\QSlow}%
      \expandafter\QSpivot{\csname QSlowindex\start\endcsname}%
      \global\expandafter\let\csname QSlowindex\start\endcsname=\QSresult%
    \fi
  \else
    \ifdim\QStest>\QSvalue
      \QShigh=\csname QShigh\start\endcsname\relax
      \ifnum\QShigh=0\relax
        \expandafter\xdef\csname QShighindex\start\endcsname{\theindex}%
        \expandafter\xdef\csname QShigh\start\endcsname{1}%
      \else
        \advance\QShigh by 1
        \expandafter\xdef\csname QShigh\start\endcsname{\the\QShigh}%
        \expandafter\QSpivot{\csname QShighindex\start\endcsname}%
        \global\expandafter\let\csname QShighindex\start\endcsname=\QSresult%
      \fi
    \else
      \QSmiddle=\csname QSmiddle\start\endcsname\relax
      \advance\QSmiddle by 1
      \expandafter\xdef\csname QSmiddle\start\endcsname{\the\QSmiddle}%
    \fi
  \fi
\egroup}
% returns (revised) tree index in \QSresult
\newcommand{\QSpivot}[1]% #1 = tree index
{\bgroup% use local values
  \edef\start{#1}%
  \QSlow=\csname QSlow\start\endcsname\relax
  \QShigh=\csname QShigh\start\endcsname\relax
  \QSmiddle=\csname QSmiddle\start\endcsname\relax
  \QSvalue=\csname QSdata\start\endcsname pt\relax
  \ifdim\QStest<\QSvalue
    \ifnum\QSlow=0
      \expandafter\xdef\csname QSlowindex\start\endcsname{\theindex}%
      \expandafter\xdef\csname QSlow\start\endcsname{1}%
      \global\let\QSresult=\start
    \else
      \ifnum\QSlow>\QShigh% pivot low
        \expandafter\let\expandafter\next\csname QSlowindex\start\endcsname
        \expandafter\let\expandafter\after\csname QShighindex\next\endcsname
        \QSlow=\csname QShigh\next\endcsname
        \expandafter\xdef\csname QSlow\start\endcsname{\the\QSlow}%
        \expandafter\xdef\csname QSlowindex\start\endcsname{\after}%
        \advance\QShigh by \QSlow
        \advance\QShigh by \QSmiddle
        \expandafter\xdef\csname QShigh\next\endcsname{\the\QShigh}%
        \expandafter\xdef\csname QShighindex\next\endcsname{\start}%
        \QSnopivot{\next}%
        \global\let\QSresult=\next%
      \else
        \advance\QSlow by 1
        \expandafter\xdef\csname QSlow\start\endcsname{\the\QSlow}%
        \expandafter\QSpivot{\csname QSlowindex\start\endcsname}%
        \global\expandafter\let\csname QSlowindex\start\endcsname=\QSresult%
        \global\let\QSresult=\start%
      \fi% end of \QSlow > \QShigh
    \fi% end of \QSlow = 0
  \else
    \ifdim\QStest>\QSvalue
      \ifnum\QShigh=0\relax
        \expandafter\xdef\csname QShighindex\start\endcsname{\theindex}%
        \expandafter\xdef\csname QShigh\start\endcsname{1}%
        \global\let\QSresult=\start%
      \else
        \ifnum \QShigh>\QSlow% pivot high
          \expandafter\let\expandafter\next\csname QShighindex\start\endcsname
          \expandafter\let\expandafter\after\csname QSlowindex\next\endcsname
          \QShigh=\csname QSlow\next\endcsname
          \expandafter\xdef\csname QShigh\start\endcsname{\the\QShigh}%
          \expandafter\xdef\csname QShighindex\start\endcsname{\after}%
          \advance\QSlow by \QShigh
          \advance\QSlow by \QSmiddle
          \expandafter\xdef\csname QSlow\next\endcsname{\the\QSlow}%
          \expandafter\xdef\csname QSlowindex\next\endcsname{\start}%
          \QSnopivot{\next}%
          \global\let\QSresult=\next
        \else
          \advance\QShigh by 1
          \expandafter\xdef\csname QShigh\start\endcsname{\the\QShigh}%
          \expandafter\QSpivot{\csname QShighindex\start\endcsname}%
          \global\expandafter\let\csname QShighindex\start\endcsname=\QSresult%
          \global\let\QSresult=\start%
        \fi% end of \QShigh > \QSlow
      \fi% end of \QShigh = 0
    \else
      \advance\QSmiddle by 1
      \expandafter\xdef\csname QSmiddle\start\endcsname{\the\QSmiddle}%
      \global\let\QSresult=\start%
    \fi% end of \QStest > \QSvalue
  \fi% end of \QStest < value
\egroup}
% return data in \QSresult
\newcommand*{\QSsearch}[1]% #1 = start of tree
{\bgroup
  \edef\start{#1}%
  \QSlow=\csname QSlow\start\endcsname\relax
  \ifnum\QSindex<\QSlow
    \expandafter\let\expandafter\next\csname QSlowindex\start\endcsname
    \QSsearch{\next}%
  \else
    \advance\QSindex by -\QSlow
    \QSmiddle=\csname QSmiddle\start\endcsname\relax
    \ifnum\QSindex<\QSmiddle
      \global\expandafter\let\expandafter\QSresult\csname QSdata\start\endcsname
    \else
      \advance\QSindex by -\QSmiddle
      \expandafter\let\expandafter\next\csname QShighindex\start\endcsname
      \QSsearch{\next}%
    \fi
  \fi
\egroup}

\begin{document}

\edef\test{{1,10,2,9,3,8,4,7,5,6}}% tikz array

\qsort{\test}
\QSresult

\end{document}

答案4

使用lambda.sty(为了娱乐)

lambda.sty包定义了完全可扩展的条件和列表。类似地,以下宏定义了二叉树和一个\BSTSort可以对列表进行树排序的宏lambda。它不完全是快速排序,但非常相似。

\documentclass{article}

\usepackage{lambda}

\makeatletter
% Tree Data type (Similar to list)
\def\Branch#1#2#3#4#5{#4{#1}{#2}{#3}}
\def\Pousse#1{\Branch{#1}\Nil\Nil} % Leaf

% In-order traversal of a tree
\def\ListTree#1{#1\ListTree@{\Nil}}
\def\ListTree@#1#2#3{\Cat{\ListTree{#2}}{\Cons{#1}{\ListTree{#3}}}}

% Insert elem #2 in tree #3 according to sorting #1
\def\InsertTree#1#2#3{#3{\InsertTree@{#1}{#2}}{\Pousse{#2}}} 
\def\InsertTree@#1#2#3#4#5{#1{#2}{#3}%
  {\Branch{#3}{\InsertTree{#1}{#2}{#4}}{#5}}% Inserted element is less than root
  {\Branch{#3}{#4}{\InsertTree{#1}{#2}{#5}}}% Inserted element is more than root
}

\def\BST#1{\Foldr{\InsertTree#1}\Nil} % Builds BST with order #1

\def\BSTSort#1{\Compose\ListTree{\BST{#1}}} % Tree-sort
\makeatother

\begin{document}
% See the lambda-list documentation for \Listize and \Unlistize
\edef\z{\Unlistize{\BSTSort\Lessthan{\Listize[3,2,8,1,2,7,4,1,3,4]}}}
\meaning\z
\end{document}

请注意,这比软件包提供的\BSTSort速度要快得多。\Insersortlambda

编辑:如前所述,lambda提供的\Lessthan宏仅比较整数。定义其他顺序很容易。例如,声明以下实际比较顺序:

\def\LessthanReal#1#2{\TeXif{\ifdim#1pt<#2pt }}

然后您就可以使用\BSTSort\LessthanReal\li它来对列表进行排序\li

相关内容