我在OWLTEST上获得了-2分,其中使用了Pylint作为样式指南。错误状态:
分支太多(17/12)
函数“合并”,第7行,
是什么意思是什么意思?
我非常努力地使该程序起作用,并从头开始编写了它。我还想知道是否有一些技术可以使清洁器更清洁和/或利用最佳实践。我知道可能有一些更好的方式来编写此代码,因为现在我的代码看起来真的很乱。 >
# -*- coding: utf-8 -*-
"""
Created on Thu Sep 3 17:55:56 2015
2048_merge_attempt1.py
@author: Rafeh
"""
def merge(nums):
'''
Takes a list as input
returns merged pairs with
non zero values shifted to the left.
[2, 0, 2, 4] should return [4, 4, 0, 0]
[0, 0, 2, 2] should return [4, 0, 0, 0]
[2, 2, 0, 0] should return [4, 0, 0, 0]
[2, 2, 2, 2, 2] should return [4, 4, 2, 0, 0]
[8, 16, 16, 8] should return [8, 32, 8, 0]
'''
slide = [] # Append non-zeroes first
for num in nums:
if num != 0:
slide.append(num)
for num in nums:
if num == 0:
slide.append(num)
pairs = []
for idx, num in enumerate(slide):
if idx == len(slide)-1:
pairs.append(num)
if len(pairs) != len(nums):
pairs.append(0)
break
if num == slide[idx+1]:
if num != 0:
pairs.append(num*2)
slide[idx+1] -= slide[idx+1]
# slide[idx+1], slide[idx+2] = slide[idx+2], slide[idx+1]
else:
pairs.append(num)
else:
pairs.append(num) # Even if they don't match you must append
slide = [] # Append non-zeroes first
for num in pairs:
if num != 0:
slide.append(num)
for num in nums:
if num == 0:
slide.append(num)
for _ in range(len(nums) - len(slide)):
if len(nums) != len(slide):
slide.append(0)
return slide
#1 楼
在Python中对成对的项目进行迭代的几种典型模式为:for prev, next in zip(nums[:-1], nums[1:]):
...
和
prev = None
for next in nums:
if prev is None:
prev = next
continue
...
以您的情况,我认为第二种方法更适合:
def merge(nums):
prev = None
store = []
for next_ in nums:
if not next_:
continue
if prev is None:
prev = next_
elif prev == next_:
store.append(prev + next_)
prev = None
else:
store.append(prev)
prev = next_
if prev is not None:
store.append(prev)
store.extend([0] * (len(nums) - len(store)))
return store
评论
\ $ \ begingroup \ $
干净的解决方案,我喜欢它。
\ $ \ endgroup \ $
– Rubik
2015年9月5日在5:52
\ $ \ begingroup \ $
continue语句有什么作用?例如,如果next_ = 5,则代码将跳至下一个if语句。如果next_ = 0,代码将继续,然后简单地跳转到下一个if语句?更新:哦!在循环的下一个迭代中继续运行!哇,学到了新东西。将此评论留在这里希望对其他人有所帮助。
\ $ \ endgroup \ $
–聪明的程序员
2015年9月5日在8:29
\ $ \ begingroup \ $
我已经读过这篇文章,并且我了解vs ==的区别在于前者是对象身份检查器,其中==检查它们是否等效。但是,我对此并不完全理解。在我的终端中,None is not None返回与None!= None相同的结果。请解释。请注意,我知道当a = [1,2]和b = [1,2]时,语句a为b将返回False,而==校验将返回True。但是,在这种情况下(无不为无),我看不到区别
\ $ \ endgroup \ $
–聪明的程序员
2015年9月5日在8:52
\ $ \ begingroup \ $
有很多好的答案。很难在@SuperBiasedMan答案和此答案之间做出选择。我决定采用此答案,因为它非常干净且具有Python风格。我了解了迭代值对的方法,这是一种特别有用的技术,以后我将可以重新实现它。我还了解了列表上的extend方法和continue函数。这可以帮助我显着清理代码并使代码超级易于阅读。就列表理解而言,另一个答案非常有用。但是,我必须选择一个,而这个简单地携带了更多有用的概念。
\ $ \ endgroup \ $
–聪明的程序员
2015年9月5日,9:11
\ $ \ begingroup \ $
实际上,我实际上会从itertools文档中建议pairwise()来进行对的迭代。可以从more-itertools包中导入实现。
\ $ \ endgroup \ $
– David Z
2015年9月5日,12:59
#2 楼
使用自动测试在文档字符串中声明:
[2, 0, 2, 4] should return [4, 4, 0, 0]
[0, 0, 2, 2] should return [4, 0, 0, 0]
[2, 2, 0, 0] should return [4, 0, 0, 0]
[2, 2, 2, 2, 2] should return [4, 4, 2, 0, 0]
[8, 16, 16, 8] should return [8, 32, 8, 0]
但是我要么
手动测试所有输入(非常无聊)
(不要个人,但代码准确性不应基于个人主观信任)
相反,您可以这样写:
>>> merge([2, 0, 2, 4])
[4, 4, 0, 0]
>>> merge([0, 0, 2, 2])
[4, 0, 0, 0]
>>> merge([2, 2, 0, 0])
[4, 0, 0, 0]
>>> merge([2, 2, 2, 2, 2])
[4, 4, 2, 0, 0]
>>> merge([8, 16, 16, 8])
[8, 32, 8, 0]
这样,每次模块执行时,
doctest
将自动运行所有测试。这项技术将为您节省以后繁琐的手动测试。评论
\ $ \ begingroup \ $
我是新手,能否请您进一步解释如何使用doctest?我读了这篇文章:docs.python.org/2/library/doctest.html,我仍然在程序底部创建了一个if名称main函数。我导入了doctest,将您在文档字符串中提到的内容包括在内,并执行了代码。但是,即使如此,它仍然无法正常工作。
\ $ \ endgroup \ $
–聪明的程序员
2015年9月4日在11:24
\ $ \ begingroup \ $
首先导入doctest。 doctest.testmod()在您的函数之后。无输出表示一切正确。
\ $ \ endgroup \ $
– Caridorc
2015年9月4日在11:31
\ $ \ begingroup \ $
最好从命令行运行python -m doctest
\ $ \ endgroup \ $
–mkrieger1
2015年9月4日在11:38
\ $ \ begingroup \ $
是的,但是随后您必须先运行doctests,然后再对其进行编辑,才能使用该模块。
\ $ \ endgroup \ $
–mkrieger1
15年9月4日在11:45
\ $ \ begingroup \ $
@ mkrieger1为什么运行doctest会打扰您?
\ $ \ endgroup \ $
– Caridorc
2015年9月4日14:03在
#3 楼
列表理解是您的朋友。它们实际上将for
循环折叠为一行,并将其直接分配到列表中,从而节省了您无用的时间。语法类似于列表括号[statement for var in iterable]
中包含的反向for循环。您首先可以这样制作slide
:稍后再次构建幻灯片时,您当然可以模仿这两行。 为什么使用此行:
slide = [num for num in nums if num != 0]
slide += [num for num in nums if num == 0]
基本上什么时候都可以成为:
slide[idx+1] -= slide[idx+1]
最后,您可以使用列表乘法来避免最终的
if not num
循环:slide[idx+1] = 0
(如果
True
相同,则它们的差值为num
无论如何,因此您无需检查长度差异)评论
\ $ \ begingroup \ $
更好:slide.extend(如果num == 0,则num为num的num)
\ $ \ endgroup \ $
–user14393
2015年9月4日23:03
\ $ \ begingroup \ $
谢谢您的回答。我从中学到了很多!我在最佳答案的评论部分中说明了使用@Jaime答案的原因。这是一个艰难的决定!我最终将使用您的建议在Coursera上提交我的代码,因为您的建议与我当前的代码和思考过程完全吻合。另外,Hurkyl很棒的建议!
\ $ \ endgroup \ $
–聪明的程序员
2015年9月5日在9:20
\ $ \ begingroup \ $
注意:Pylint对我大喊,因为我使用i而不是idx。 @超人
\ $ \ endgroup \ $
–聪明的程序员
2015年9月5日,12:54
\ $ \ begingroup \ $
@Rafeh Glad我帮助了!而我的使用可能会受到一些质疑。我确实经常看到它,所以是否听Pylint取决于您。我想我应该注意idx并没有什么问题,很明显,我只是喜欢输入更少的内容,并且使用i进行这样的简单循环也从未遇到过麻烦。
\ $ \ endgroup \ $
–SuperBiasedMan
2015年9月11日9:37
\ $ \ begingroup \ $
我完全同意!仅仅是我为此丢掉了分数,仅此而已。否则我在for循环中使用“ i”我自己就不会有问题!
\ $ \ endgroup \ $
–聪明的程序员
2015年9月11日9:38在
#4 楼
是什么意思?
分支是指执行下一行代码以外的内容时。根据OWLTEST,您有17个分支机构,其中12个分支机构是可接受的最大值。
长话短说,它不喜欢您的风格。同一函数中有许多循环和许多if。拆分功能将大大提高可读性。
它可能抱怨的另一点是重复。彼此次超过
nums
。您是否考虑过一次迭代,将所有num != 0
附加到第一张幻灯片上,将所有num == 0
附加到第二张幻灯片上,然后再将这些幻灯片粘贴在一起? 。迭代很昂贵。#5 楼
我必须承认,我对您的代码感到很困惑。它需要很多变量,周期和if语句。单看它,我很难理解它的作用。为了使用Python进行一些练习,我尝试做同样的练习,然后我想到了这一点(结合了Caridorc的答案):
这是与原始代码相比的主要区别:
没有任何一行看起来与其他任何代码相似。通常,许多类似的行表示存在更好的方法来使代码更轻便,更易于阅读。
最小化使用额外的变量。每次声明新变量时,都必须了解您打算将其用于什么!通常,使用许多支持变量(尤其是复杂的支持变量)也会影响性能。
使用while循环而不是for循环。当您不知道先验范围时,while是一个完美的循环。它的语法比for循环更简单,如果保持简单,也很容易阅读。
避免缩进级别过多。您越深入,代码就越难理解。
我希望这有助于继续练习编写越来越好的代码!如评论中所建议:
def merge(nums):
'''
Takes a list as input
returns merged pairs with
non zero values shifted to the left.
test with:
python -m doctest 2048.py
No output == OK!
>>> merge([2, 0, 2, 4])
[4, 4, 0, 0]
>>> merge([0, 0, 2, 2])
[4, 0, 0, 0]
>>> merge([2, 2, 0, 0])
[4, 0, 0, 0]
>>> merge([2, 2, 2, 2, 2])
[4, 4, 2, 0, 0]
>>> merge([8, 16, 16, 8])
[8, 32, 8, 0]
'''
# save the initial length of the list
size = len(nums)
# remove all the zeroes - looked up on stackoverflow
nums[:] = (value for value in nums if value != 0)
# now do the merging, use an index to run through the list
i = 0
while (i < len(nums)-1 ):
if (nums[i]==nums[i+1]): # if a number is the same as the following
nums[i] *= 2 # double it
del nums[i+1] # remove the following
i += 1
# restore the initial list length appending zeroes
while (len(nums) < size):
nums.append(0)
# done
return nums
可以在一个额外的列表中将c语言循环进行更多的pythonic复制,但是我更喜欢这样。 >
评论
\ $ \ begingroup \ $
最后一个while(len(nums)<...可以写成nums.extend([0] *(size-len(nums))))更好,而while(i <...应该是为i在range(len(nums))中编写,最后留下不必要的括号,最后,您应该列出一个新列表,而不要使用nums [:]
\ $ \ endgroup \ $
–WorldSEnder
2015年9月5日,0:59
\ $ \ begingroup \ $
@WorldSEnder的第一点是。对于第二个否定:随着您的建议从列表中删除,索引超出范围。
\ $ \ endgroup \ $
– DarioP
2015年9月5日,下午1:05
\ $ \ begingroup \ $
在这里,您正在就地修改参数nums,同时还返回了它。总的来说,尤其是在这种情况下,我建议不要这样做。
\ $ \ endgroup \ $
– Sjoerd Job Postmus
2015年9月5日于7:44
\ $ \ begingroup \ $
感谢您非常有用的指针@Dario。我不明白分配给nums [:]与nums的原因。如果您能详细说明一下,那就太好了。我同意WorldSEnder使用.extend方法。那太整齐了。
\ $ \ endgroup \ $
–聪明的程序员
2015年9月5日于10:29
#6 楼
在最初的两个循环中,我们仅需要第一个循环。我们真的只想要非零值;直到最后,我们不需要处理零。def merge(nums):
slide = []
for num in nums:
if num != 0:
slide.append(num)
接下来,我们将摆脱主要的for循环。它将变成一个while循环;我们将自行处理。
pairs = []
idx = 0
while idx < len(slide):
num = slide[idx]
if idx == len(slide)-1:
pairs.append(num)
break
我们也不需要在这里检查长度,稍后我们将处理尾部填充。我们还保留
break
,这是我们摆脱循环的方式。现在进行主检查。我们不再麻烦检查零相等性,我们已经知道
slide
中没有零。 if num == slide[idx+1]:
pairs.append(num*2)
idx += 1
请注意额外的增量,因此我们跳过了下一个数字。我们具有与您相同的标准
else
子句。最后,我们自己增加计数器。 else:
pairs.append(num)
idx += 1
现在,这时,我们有了所需的正确值,我们只需要在右边用零填充即可。幸运的是,您已经可以做到这一点。
for _ in range(len(nums) - len(pairs)):
pairs.append(0)
return pairs
我刚刚将变量名称从
slide
更改回pairs
。此时,以最初使用的方式重用变量名是一种不好的做法,应避免使用它。 此外,我们不需要循环中的长度检查。
所以,总的来说,代码看起来像是
def merge(nums):
slide = []
for num in nums:
if num != 0:
slide.append(num)
pairs = []
idx = 0
while idx < len(slide):
num = slide[idx]
if idx == len(slide)-1:
pairs.append(num)
break
if num == slide[idx+1]:
pairs.append(num*2)
idx += 1
else:
pairs.append(num)
idx += 1
for _ in range(len(nums) - len(pairs)):
pairs.append(0)
return pairs
您会注意到,随着函数体的变小,它变得更容易阅读。同样,在三个变量之间重用两个名称会使代码难以遵循。另外,将功能的“段落”分开的一些空格使它更易于阅读。
您的风格还不错。缩进是一致的,您坚持使用公认的python缩写(例如
idx
,虽然简单地使用i
也可以接受),并且使用了体面的变量名(除了我已经提到的问题)。 评论
\ $ \ begingroup \ $
感谢您的深刻见解。这个while循环肯定会帮助我保持代码更干净!是的,当我再次尝试遍历代码时,我也意识到我不需要0附加器,因为我可以简单地反向工程并在最后附加所有需要的0。对于像我这样的初学者/中级程序员,您的答案非常彻底,非常有用!
\ $ \ endgroup \ $
–聪明的程序员
2015年9月5日在11:13
评论
缩进有点奇怪,但是我不确定它是仅来自复制/粘贴还是属于您要查看的内容,因此我不想编辑它。特别是:编码行以两个空格缩进,而代码的其余部分以另外两个空格缩进,两者都是多余的。同样在第40行中,pairs.append(num)的缩进太深了一个级别(四个空格),这可能会产生误导,尤其是在Python中。@ mkrieger1是的,第40行缩进不正确。感谢您抓住我现在在代码中修复的问题。很奇怪,没有样式指南分析能抓住它。我经历了5个不同的过程。完成代码后,是否可以自动格式化或重组代码,使其符合最佳样式指南标准?至于缩进,它不是我的实际代码中那样。不知道为什么在这里如此缩进。
编辑帖子时,缩进四个空格的所有内容均视为代码块。因此,在编写问题时,第一行缩进了六个空格,其余行缩进了八个空格。插入代码块的一种安全方法是粘贴代码块而没有任何额外的缩进,然后将其突出显示并按Ctrl-K或按{}按钮,这会将所有内容统一缩进四个空格。
那就是我尝试过的...没有用?我拿走了整个代码,并粘贴了0个缩进,然后按Ctrl-K,它不起作用。所以我最终放弃了。 :(
看看其中一些代码。