以下链接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[:])
答案 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;
}
}
如果程序员不知道add
和addBad
中发生了什么,这些看起来都很相似。并且add
和addBad
的实现可以互换,代码仍然可以编译。 Python可以是类似的,因为通过引用传递似乎发生了很多而没有明确声明它。
答案 2 :(得分:2)
一切正常,因为你只是给出一个论点的副本。糟糕的是保持实现简单,使用pop
和列表理解,您需要传递原始列表的多个副本。这不像排序那样具有内存效率。
顺便说一句,不要将list
用作变量名。
答案 3 :(得分:1)
有时,您希望就地排序列表。有时,您需要列表的已排序副本(例如,如果您还想保留原始未排序的订单)。因此,Python标准库允许您同时编写lst.sort()
和sorted(lst)
。
但是在Python中,一个函数既改变它的参数又返回一个值,这是不寻常的。更常见的约定是Command-Query Separation,其中mutator函数返回None
。例如,在list
课程中,append
,clear
,extend
,remove
,reverse
和sort
方法,添加,删除或重新排序列表中的项目,都返回None
。
pop
方法是一个例外,practicality beats purity的情况。