Python栈实验中InsertAtCommand撤销功能故障排查求助
栈操作撤销功能的Bug排查求助
正在学习算法与数据结构课程,栈相关实验中遇到问题:我的InsertAtCommand对象需要实现从栈中移除指定索引元素,并能撤销该操作(将移除的元素恢复至原索引)。目前有2个测试用例失败,均出现列表缺失一个元素的情况,猜测是撤销时未正确插入最后一个元素,但不确定问题根源。
已尝试修改InsertAtCommand的undo方法,原代码:
def undo(self): del self.source_list[self.index]
修改为:
def undo(self): print("Before undo - List:", self.source_list) print("Before undo - Removed item:", self.removed_item) #remove the new item that was inserted during the execute method self.source_list.pop(self.index) #reinsert the previously removed item at the original index self.source_list.insert(self.index, self.removed_item) print("After undo - List:", self.source_list)
但测试输出仍显示撤销后缺失元素,示例失败输出如下:
Adding chips PASS: Undo stack size is 1 Adding cookies PASS: Undo stack size is 2 Adding waffles PASS: Undo stack size is 3 Adding syrup PASS: Undo stack size is 4 Adding ice cream PASS: Undo stack size is 5 Adding lettuce PASS: Undo stack size is 6 Verfied list content: chips,cookies,waffles,syrup,ice cream,lettuce Removing at index 2 PASS: Undo stack size is 7 Verfied list content: chips,cookies,syrup,ice cream,lettuce Removing at index 2 PASS: Undo stack size is 8 Verfied list content: chips,cookies,ice cream,lettuce Removing at index 3 PASS: Undo stack size is 9 Verfied list content: chips,cookies,ice cream Removing at index 0 PASS: Undo stack size is 10 Verfied list content: cookies,ice cream Executing undo PASS: Undo stack size is 9 FAIL: Expected items: ['chips', 'cookies', 'ice cream'] Actual items: ['cookies', 'ice cream']
核心代码
InsertAtCommand类
class InsertAtCommand(UndoCommand): def __init__(self, source, index, new_item): super().__init__() self.source_list = source self.index = index self.new_item = new_item self.removed_item = None def execute(self): #remove the item at the specified index and store it self.removed_item = self.source_list.pop(self.index) #Insert the new item at the specified index self.source_list.insert(self.index, self.new_item) def undo(self): #remove the new item that was inserted during the execute #method self.source_list.pop(self.index) #reinsert the previously removed item at the original index self.source_list.insert(self.index, self.removed_item)
调用InsertAtCommand的代码
def remove_at_with_undo(self, removal_index): #check if index is valid if 0 <= removal_index < len(self.list_items): #store the removed item removed_item = self.list_items[removal_index] #remove the item at the specified index del self.list_items[removal_index] #push an InsertAtCommnad onto the undo stack to undo the removal insert_command = InsertAtCommand(self.list_items[:], removal_index, removed_item) self.undo_stack.push(insert_command)
执行撤销的代码
def execute_undo(self): if self.undo_stack: #pop the last command from the undo stack undo_command = self.undo_stack.pop() #Check if the undo command is an instance of RemoveLastCommand if isinstance(undo_command, RemoveLastCommand): #execute the undo command undo_command.execute() elif isinstance(undo_command, SwapCommand): undo_command.undo() elif isinstance(undo_command, InsertAtCommand): undo_command.undo()
排查思路与解决建议
致命问题:操作的是列表副本而非原列表:在
remove_at_with_undo中,创建InsertAtCommand时传的是self.list_items[:],这是原列表的浅拷贝。也就是说,InsertAtCommand的所有操作都只作用在这个副本上,完全不会修改原列表self.list_items,撤销自然不会生效。必须直接传原列表self.list_items,不要用切片拷贝。命令逻辑完全搞反:当前
InsertAtCommand的execute方法是“移除原元素,插入新元素”,但你的需求是:执行remove_at操作后,撤销动作应该是“把被移除的元素插回原索引”。但你在remove_at_with_undo中已经手动用del删除了元素,然后又让InsertAtCommand的execute再执行一次pop,等于在副本上又做了一次错误的删除,逻辑完全混乱。修正步骤:
- 删除
remove_at_with_undo中的del self.list_items[removal_index],把删除操作交给InsertAtCommand的execute方法来执行,确保所有修改都通过命令完成,这样撤销才能正确追踪。 - 重构
InsertAtCommand的职责:改名为RemoveAtCommand更贴合功能,execute方法负责移除指定索引的元素并保存,undo方法负责将保存的元素插回原索引。 - 确保命令操作的是原列表:创建命令时传入
self.list_items,而非副本。 - 检查
execute_undo中的逻辑:对于RemoveAtCommand(原InsertAtCommand),调用undo方法即可,不需要额外判断混淆逻辑。
- 删除
内容的提问来源于stack exchange,提问作者user24995660
相关产品推荐
相关产品推荐

