《Think Python》第10章练习6卡住了
练习:
写一个叫做
is_sorted
的函数,这个函数接收一个列表作为参数。如果这个列表是按从小到大的顺序排列的,就返回True
,否则返回False
。你可以假设(作为前提条件)列表里的元素可以用比较运算符<
、>
等进行比较。比如,
is_sorted([1,2,2])
应该返回True
,而is_sorted(['b', 'a'])
应该返回False
。
到目前为止,我写了:
def is_sorted(stuff):
for i in stuff:
if stuff[i+1] > stuff[i]:
return True
else:
return False
numbers = [1, 0, 5, 2, 8]
print is_sorted(numbers)
但是每次我改变数字列表的顺序时,它似乎总是返回 True
。我该怎么改才能让它正常工作呢?
6 个回答
我知道这个话题已经讨论很久了,但我想改进一下我的解决方案。
我想确保在一个主要是字符串的列表中,不会偷偷混入整数,或者在一个主要是整数的列表中,不会混入字符串。比如说,像这样的列表:['a', 'b', 2]
我写了一个Python程序来解决这个问题,但我觉得它有点复杂。有没有更好的解决办法呢?
def is_sorted(stuff):
is_int = 1
not_int = 0
if type(stuff[0]) == int:
of_type = is_int
else:
of_type = not_int
for i in range(1,len(stuff)):
if (of_type == is_int and (type(stuff[i - 1]) == int and type(stuff[i]) == int)) or \
(of_type == not_int and (type(stuff[i - 1]) == str and type(stuff[i]) == str)):
if stuff[i - 1] > stuff[i]:
return False
else:
print "I saw what you did there!"
return False
return True
print is_sorted(['b', 'a']) //False
print is_sorted(['a', 'b']) //True
print is_sorted([1, 2]) //True
print is_sorted(['a', 'b', 2]) //False; "I saw what you did there!"
看起来你把 for item in stuff
和 for i in range(len(stuff))
混淆了。
假设 stuff = ['a', 'b', 'c', 'd']
。
使用
for item in stuff
时,你会一个一个地遍历stuff
里的每个元素:'a'、'b'、'c' 和 'd'。使用
for i in range(len(stuff))
时,你会遍历stuff
的每个索引:0、1、2 和 3。
需要注意的是,变量名 item
和 i
是这两种写法的常用名称,但并不是必须的——你可以用任何你想要的名字来替换 item
和 i
。所以你的第二行代码 (for i in stuff
) 实际上是在执行上面提到的第一个情况,而不是你期待的第二个情况。
要让你的代码执行第二个情况,你需要在第二行代码中使用 range(len(stuff))
,而不是直接用 stuff
。
基于@jonrsharpe的回答,
这是一个很好的使用itertools
中的pairwise
方法的例子:
from itertools import tee, izip
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None) #or b.next() on Python 2.5 and below
return izip(a, b)
def is_sorted(iterable):
return all(a <= b for a,b in pairwise(iterable))
这样做可以避免使用索引访问或切片,这样你就可以在生成器上操作,显著减少内存占用。
is_sorted(xrange(5000000))
不过,不幸的是,这对像is_sorted(count(0))
这样的无限生成器是没用的。
for i in stuff
这个写法会一个一个地遍历每个元素。如果你想要遍历的是这些元素的位置,那么你需要把循环改成
for i in range(len(stuff))
接下来,你不想在遇到比当前元素大的后续元素时就返回True。你应该在检查每一对相邻元素后再返回True,所以可以写成这样:
def is_sorted(stuff):
for i in range(1,len(stuff)):
if stuff[i - 1] > stuff[i]:
return False
return True
这一行代码
for i in stuff:
是用来遍历 items,而不是 indices(索引)。如果你把 5
或 8
放在前面,就会出现 IndexError
(索引错误)。而且,如果第一个项目的检查通过,你就 return
(返回)了,这样太早了!你可以在 任何 项目不符合顺序时返回 False
,但 所有 项目都要按顺序才能返回 True
。
相反,可以试着使用 enumerate
来同时获取项目和索引:
def is_sorted(stuff):
for index, item in enumerate(stuff):
try:
if item > stuff[index + 1]:
return False
except IndexError:
return True
另外,也可以使用 zip
来成对比较:
def is_sorted(stuff):
return all(b >= a for a, b in
zip(stuff, stuff[1:]))