为什么在快速排序算法中变异列表的副作用不好?

时间:2013-07-12 03:41:33

标签: python algorithm list quicksort

以下链接http://en.literateprograms.org/Quicksort_%28Python%29以下内容 提出索赔。我们利用pop操作删除我们选择的pivot。这有一个令人遗憾的副作用,即改变传递给sort函数的原始列表。为什么这是一个不幸的副作用?即使我直接在下面调用qsort函数,我也会将排序列表作为输出,因为我们有return语句。

from random import randrange       
def qsort1a(list):
    """
    Quicksort using list comprehensions and randomized pivot
    >>> qsort1a<<docstring test numeric input>>
    <<docstring test numeric output>>
    >>> qsort1a<<docstring test string input>>
    <<docstring test string output>>
    """
    def qsort(list):
        if list == []: 
            return []
        else:
            pivot = list.pop(randrange(len(list)))
            lesser = qsort([l for l in list if l < pivot])
            greater = qsort([l for l in list if l >= pivot])
            return lesser + [pivot] + greater
    return qsort(list[:])

4 个答案:

答案 0 :(得分:5)

实际上,您通过将qsort1a定义为内部qsort的包装来“修复了坏事”。您的包装器使用切片运算符创建原始列表的副本:

return qsort(list[:])

如果删除复制操作,请将最后一行读为:

return qsort(list)

然后运行它,你可以看到为什么它“坏”:

>>> from qsort1a import qsort1a
>>> orig = [3, 17, 4, 0, 2]
>>> qsort1a(orig)
[0, 2, 3, 4, 17]
>>> orig
[3, 17, 4, 2]
>>> 

输出仍然已排序,但原始列表orig已更改。

(另外,正如其他人所说,将list这样的名称重新用作局部变量通常是不明智的,因为它使得访问Python的内置list函数变得不必要地困难。)

答案 1 :(得分:4)

这很糟糕,因为无论如何你都会得到一个新的清单。为什么有两个相同列表的副本?如果您想继续使用原始未排序列表,该怎么办?具有返回值的函数可能不应该改变它们的参数。

编辑:我将提供一个例子,为什么变异的参数可能会令人困惑。在c ++中。

添加琐碎的添加功能。

int add(int a, int b)
{
   a += b;
   return a;
}

int addBad(int &a, int &b)
{
   a += b;
   return a;
}

for(int i=0;i<10;++i)
{
   for(int j=0;j<10;++j)
   {
      cout << add(i,j) << endl;
   }
}

for(int i=0;i<10;++i)
{
   for(int j=0;j<10;++j)
   {
      cout << addBad(i,j) << endl;
   }
}

如果程序员不知道addaddBad中发生了什么,这些看起来都很相似。并且addaddBad的实现可以互换,代码仍然可以编译。 Python可以是类似的,因为通过引用传递似乎发生了很多而没有明确声明它。

答案 2 :(得分:2)

一切正常,因为你只是给出一个论点的副本。糟糕的是保持实现简单,使用pop和列表理解,您需要传递原始列表的多个副本。这不像排序那样具有内存效率。

顺便说一句,不要将list用作变量名。

答案 3 :(得分:1)

有时,您希望就地排序列表。有时,您需要列表的已排序副本(例如,如果您还想保留原始未排序的订单)。因此,Python标准库允许您同时编写lst.sort()sorted(lst)

但是在Python中,一个函数既改变它的参数返回一个值,这是不寻常的。更常见的约定是Command-Query Separation,其中mutator函数返回None。例如,在list课程中,appendclearextendremovereversesort方法,添加,删除或重新排序列表中的项目,都返回None

pop方法是一个例外,practicality beats purity的情况。