我正在开发一个管理列表的程序,并且必须能够插入新的项目,并删除项目的范围。我希望用户能够根据列表的当前状态指定插入和删除,而不必担心以后的操作如何需要一个偏移索引才能处理正确的项。
例:
删除: 4-5 插入: 2,6
将在索引2之前插入一个元素,在原来的元素6之前插入另一个元素,并删除最初位于4和5的项。
我想将这些指令转换为删除后插入的列表,这些插入可以连续执行(上面的操作将变成:
删除4,删除4,插入2,插入4
由于4
上的删除移动了下面的操作,所以下一个删除也会在4
上执行。2
的插入使6
的插入右移,但前两个减法使其左移。
我正在寻找一个(希望是可读的)公式,将这些插入和删除的索引转换为它们在delete
和insert
列表中的形式。
到目前为止,我有一个相当不可读的python函数,它很难维护,但它似乎输出了正确的值:
ii = jj = kk = 0 # list indices
plus = minus = 0 # plus / minus offset
while ii < len(dellist) or jj < len(addlist):
if jj < len(addlist): # in the list
if addlist[jj] == kk: # at the specified index
addlist[jj] -= minus # deletions happen first, so shift it back
addlist[jj] += plus # then additions happen, so shift it forward
jj += 1 # next addition
plus += 1 # one more item inserted before the others
if ii < len(dellist): # in the list
if dellist[ii] == kk: # at the specified index
dellist[ii] -= minus # previous deletions, so shift back
ii += 1 # next deletion
minus += 1 # one more item removed from before the others
kk += 1 # next list element
我主要关注的是有可读的代码,这些代码以后可以轻松修改;理想情况下,我还需要一些更模块化的代码,尽管我不知道在这种情况下如何实现这一点。
发布于 2016-05-24 07:19:35
并不是真正的代码评审,而是另一种建议:
为什么不让处理器反向处理列表呢?考虑到所建议的“修改”,先按位置对它们进行排序,然后是最大位置。然后,处理器可以沿着这条线前进,而不必在前面的步骤中处理对列表的修改。
因此,逻辑将更多地如下:
for (pos, mod) in sorted(modifications, reverse=True):
if mod == 'del':
del lst[pos]
elif mod == 'ins':
lst.insert(pos, "some item")
else:
raise ValueError("unknown action") # Or some other handling.
发布于 2016-05-23 14:46:46
所以我把你的代码变成了一个函数,然后在你给出的例子中运行它。我得到了以下代码:
def shift_indexes(dellist, addlist):
ii = jj = kk = 0
plus = minus = 0
while ii < len(dellist) or jj < len(addlist):
if jj < len(addlist):
if addlist[jj] == kk:
addlist[jj] -= minus
addlist[jj] += plus
jj += 1
plus += 1
if ii < len(dellist):
if dellist[ii] == kk:
dellist[ii] -= minus
ii += 1
minus += 1
kk += 1
return dellist, addlist
dellist = [4, 5]
addlist = [2, 6]
print('del: {}\nadd: {}'.format(*shift_indexes(dellist, addlist)))
您提供的输入运行良好,但当我将addlist
更改为6, 2
而不是2, 6
时,情况就不一样了。所以我会“白方块”测试你的代码:
if addlist[0] == 0: # 6 == 0 : False
kk += 1 # kk = 1
if addlist[0] == 1: # 6 == 1 : False
kk += 1 # kk = 2
if addlist[0] == 2: # 6 == 2 : False
kk += 1 # kk = 3
if addlist[0] == 3: # 6 == 3 : False
kk += 1 # kk = 4
if addlist[0] == 4: # 6 == 4 : False
kk += 1 # kk = 5
if addlist[0] == 5: # 6 == 5 : False
kk += 1 # kk = 6
if addlist[0] == 6: # 6 == 6 : True
jj += 1 # jj = 1
kk += 1 # kk = 7
if addlist[1] == 7: # 2 == 7 : False
kk += 1 # kk = 8
if addlist[1] == 8: # 2 == 8 : False
... # loop for ever
错误很明显!
好的,让我们改变这个。我们知道删除和添加是按顺序进行的,而且顺序很重要!我们应该能够猜测插入和删除应该能够发生在任何地方。我们需要算法来处理任意顺序的数字。
所以老实说,我会推荐一种新算法。
为了简化所有代码,我强烈建议您更改输入,这个输入应该是一个列表,按照您想要添加和删除的顺序排列。此列表中的项应该是(operation, index)
。
若要保存您,请了解如何更改此窗体的当前输入。你想:
zip
将列表放在一起。因此:
list_ = [list(i) for i in
zip(['del'] * len(dellist) + ['add'] * len(addlist), dellist + addlist)]
不太好读,但它使算法更好!你的正负方法不起作用,所以我不打算谈修改它。
因此,最简单的方法就是在迭代list_
的同时转换它。迭代时,我们想要通过enumerate
传递它--这是为了在list_
中得到索引,我们还需要操作符和我们想要相对移动的索引。这导致:
for i, (operation, curr_index) in enumerate(list_):
因此,我们想跳过任何我们不知道的操作。如果您稍后决定删除可怕的zip
列表理解内容。这是一个简单的if ...: continue
:
if operation not in ('add', 'del'):
continue
现在是算法的主要部分。如果运算符添加的是“否则-1”,则希望将该项之后的索引更改为+1。
change = 1 if operation == 'add' else -1
然后,如果索引大于当前项,则希望将更改添加到当前项之后的所有项中。因此:
i += 1
for j, (_, index) in enumerate(list_[i:], i):
if index >= curr_index:
list_[j][1] += change
如果我们把这一切合并在一起,你应该得到:
def shift_indexes(dellist, addlist):
# Transform input to better format
list_ = [list(i) for i in
zip(['del'] * len(dellist) + ['add'] * len(addlist), dellist + addlist)]
for i, (operation, curr_index) in enumerate(list_, 1):
if operation not in ('add', 'del'):
continue
change = 1 if operation == 'add' else -1
for j, (_, index) in enumerate(list_[i:], i):
if index >= curr_index:
list_[j][1] += change
return list_
如果您拥有的不仅仅是add和del,那么您可能需要使用字典。
我们现有的“字典”是:
d = {
'add': 1,
'del': -1
}
要使用本词典,您需要更改if和如何获得change
。如果更改不是有效项,则可以使用dict.get
获取更改或None
,然后可以使用:
change = d.get(operator, None)
if change is None:
continue
不管怎样,这比你所做的要简单得多。
https://codereview.stackexchange.com/questions/129025
复制相似问题