最近,我写了这段代码。这将获得一个布尔值列表,其中包含所有False
值散布在n
岛 True
值的值。像这样:
[False, False, False, True, True, False, False, False, True, True, True, False]
# [island 1] [ island 2 ]
现在我想将这些分成n
列表,其中每个岛都被分离。对于上面的列表,我会得到:
[[False, False, False, True, True, False, False, False, False, False, False, False], #-> list has island 1
[False, False, False, False, False, False, False, False, True, True, True, False]] #-> list has island 2
这是我用来解决问题的程序:
import itertools as it
def getPosList(x):
segs = [ (s[0], list(s[1])) for s in it.groupby(x)]
allSegs = [ [] for s in segs if s[0] ]
i = 0
for s in segs:
s1, s2 = s
s2 = list(s2)
if s1:
for j, s in enumerate(allSegs):
if i == j: s += s2
else: s += len(s2)*[False]
i += 1
else:
for s in allSegs: s += s2
return allSegs
if __name__ == '__main__':
print getPosList([False, False, False, True, True, False, False, False, True, True, True, False])
这看起来非常具有循环和虚拟变量的非语音。我觉得应该有一个比我刚写的更优雅的解决方案。与dropwhile
,reduce
等有关。但是,我似乎无法想出更好的东西。有关改进的建议吗?每当我看到这段丑陋的代码时,我都会畏缩。
编辑:
我从feseje那里获取灵感并提出了一个递归版本。
import itertools as it
def ppListTF(xs):
return '[' + '|'.join([ '#' if x else ' ' for x in xs ]) + ']'
def getPosList1(x):
'''
'''
if x == []: return []
curX = list(it.takewhile(lambda m: m == x[0] , x))
nextVals = getPosList1(list(it.dropwhile(lambda m: m == x[0], x)))
curX1 = [False]*len(curX)
if nextVals:
curX2 = [False]*len(nextVals[0])
if x[0]:
# The first element is True. Split the list here
if nextVals: return [curX+curX2] + [ curX1+j for j in nextVals]
else: return [curX] + [curX1]
else:
# Just add the series of False to whatever is returned
if nextVals: return [ curX+j for j in nextVals]
else: return [curX]
if __name__ == '__main__':
inp = [False, False, False, True, True, False, False, False, True, True, True, False, True, True, False, False, False, False]
val = getPosList1(inp)
if any(inp):
val = filter( any, val )
print 'i', ppListTF(inp)
for i, v in enumerate(val):
print i, ppListTF(v)
我仍然喜欢Hrabals的回答。这是最好的。感谢所有发布答案的人。
答案 0 :(得分:2)
为了简洁,我将使用0和1。这是Python 2.7。并不是因为结果如此疯狂,但也许它会给你一些其他想法。
from itertools import *
a = map(int, list('1000111001001'))
start_indices = [i[0] for i in list(enumerate(zip(a, [0]+a))) if i[1] == (1, 0)]
# [0, 4, 9, 12]
get_island_length = lambda i: len(list(takewhile(lambda x: x, a[i:])))
pad = lambda b: b + [0]*(len(a)-len(b))
islands = [pad([0]*i + [1]*get_island_length(i)) for i in start_indices]
[''.join(map(str, i)) for i in islands]
# ['1000000000000', '0000111000000', '0000000001000', '0000000000001']
答案 1 :(得分:2)
与克里斯·马丁的答案类似的方法,但没有lambdas,只是列表理解:
islands = [False, False, False, True, True, False, False, False, True, True, True, False]
islands_starts = [v[0]+1 for v in list(enumerate(zip(islands, islands[1:]))) if v[1] == (False, True)]
#[3, 8]
islands_ends = [v[0] for v in list(enumerate(zip(islands, islands[1:]))) if v[1] == (True, False)]
#[4, 10]
separated_islands = [[False if p not in range(i[0],i[1]+1) else True for p in range(0,len(islands))] for i in zip(islands_starts,islands_ends)]
#[[False, False, False, True, True, False, False, False, False, False, False, False],
#[False, False, False, False, False, False, False, False, True, True, True, False]]
答案 2 :(得分:1)
虽然我同意@grc这在这里不是一个合适的问题,但我想指出你只需要一次迭代即可。你可以逐个处理这些元素,并在你遇到一个岛时创建一个新的列表。
def separateIslands(islandSegments):
separateIslands = []
empty = [False for x in islandSegments]
for i in range(0, len(islandSegments)-1):
if islandSegments[i]:
if i == 0 or not islandSegments[i-1]:
separateIslands.append(empty)
separateIslands[len(separateIslands) - 1][i] = True;
return separateIslands
islandSegments = [False, False, False, True, True, False, False, False, True, True, True, False]
print separateIslands(islandSegments)
这可能不是更多的“pythonian”,但仍然是一种简化:)