我在阅读 diveintopython 的两个小时后,我实现了一个简单版本的快速排序。
import operator
def even(num):
return operator.mod(num,2) == 0
def last(list):
return len(list)-1
def median(list):
if even(len(list)):
return len(list)/2 - 1
else:
return len(list)/2
def sort(list, pivot_selector):
if len(list) <= 1:
return list
else:
i = pivot_selector(list)
pivot = list[i]
less, greater, equal = [], [], []
for x in list:
if x < pivot:
less.append( x )
elif x == pivot:
equal.append( x )
else:
greater.append( x )
return sort(less, pivot_selector) + equal + sort(greater, pivot_selector)
if __name__ == "__main__":
print sort([5,4,3,2],median)
print sort([],median)
print sort([2],median)
print sort([3,2],median)
print sort([3,2,3],median)
print sort([1,3,2],median)
print sort([1,'a',0],median)
print sort([None,1,0],median)
5个问题:
答案 0 :(得分:5)
1 - 此代码驻留在名为quicksort.py的文件中。如何将该方法隐藏起来,甚至将其导出为公众。
惯例是将函数命名为模块with an underscore at the beginning的私有函数。
2 - 传递* pivot_selector *作为参数是pythonic吗?
由于Python具有第一类函数,因此可以将函数作为参数传递。
3 - 我的快速配置实施有什么不对或不理想吗?
使用equal
列表对我来说似乎不是传统的。 Usually items equal to the pivot end up in the greater
list.
4 - 您如何允许用户指定一个比较器来对列表元素强制执行自定义排序?
标准Python sort()
和sorted()
函数具有比较函数的可选参数。这似乎是最好的方法。
5 - 是否有一种pythonic方法来强制执行list参数必须包含同类型的约束?
在Python中,您通常不担心这一点。 Python有concept of duck-typing,所以如果一个对象做了它应该做的事情,我们不担心事先检查它的类型。这通常表达为“请求宽恕比获得许可更容易。”
因此,让模块的用户担心如果他们传入一个无法相互比较的对象列表,则会抛出异常。
答案 1 :(得分:4)
关于代码的一些半随机说明:
operator.mod
中明确even
而不只是%
?len(list)/2 - 1
中median
真的很重要吗?如果你有一个长度为4的列表,为什么索引2的中位数小于索引1?此外,middle
将是一个更合适的名称,因为该函数并不真正计算中位数。在快速排序列表中查找实际中位数是一个非常复杂的问题,通常是近似的。sort
。class UserID
进行比较并提供适当的方法/操作符,那就让他吧。答案 2 :(得分:1)
我觉得这很好。我喜欢枢轴选择器的函数参数。
一些意见:
list
或sort
even
函数看起来有点多余。答案 3 :(得分:0)
sort
的{{1}}参数)答案 4 :(得分:0)
我唯一想补充的是,创建三个列表并一次增加一个元素看起来相当不理想。典型的快速排序实现(在Python中或不在Python中)会在现有列表中移动元素。
答案 5 :(得分:0)
2:是的。但使用默认值可能会更好。
5:isinstance。但不是一个好的选择。确保他们 都有必要的界面就好了,你没有 必须监控他们的类型。
答案 6 :(得分:0)
实现排序算法的Pythonic方法不是。 ;)内置的sort
有一个原因,那就是只有一种方法可以做到。
那说:
对于简单模数运算,无需import operator
。 Pythonistas理解%
符号。
但我们甚至不需要单独检查列表长度的奇偶校验,因为Python会进行整数除法。
在这一点上,将median
真正用于函数是没有意义的,因为它所做的工作非常简单。我们也不需要last
,因为Python允许您使用负数索引列表并从最后开始计算。 mylist[-1]
是获取最后一个元素的惯用的Pythonic方法。
一般编程实践:不要做任何事情 - 即在每次计算中为每个中间结果设置一个单独的变量。变量适用于具有足够重要名称的事物。如果你不能想到一个比“我”更具描述性的名字,那么这可能表明你并不需要打破它。
正如其他人所提到的,不要影响内置。应该列出的变量的一个常见约定是将它们称为a_list
。 (这来自Smalltalk世界,IIRC。)
使用列表推导(和/或内置函数map
和filter
来尝试)尽可能将列表处理到其他列表中。
我会说'pivot_selector'应该实际计算阈值,而不是位置。毕竟,没有理由认为算法正确性要求'pivot'实际上在数组中:)
def middle(a_list): return a_list[(len(a_list) - 1) / 2]
def last(a_list): return a_list[-1]
def quick_sort(a_list, pivot_selector):
if len(a_list) <= 1: return a_list
pivot = pivot_selector(a_list)
return (
quick_sort([x for x in list if x < pivot], pivot_selector) +
[x for x in list if x == pivot] +
quick_sort([x for x in list if x > pivot], pivot_selector)
)