You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,等于在副本上又做了一次错误的删除,逻辑完全混乱。

  • 修正步骤:

    1. 删除remove_at_with_undo中的del self.list_items[removal_index],把删除操作交给InsertAtCommand的execute方法来执行,确保所有修改都通过命令完成,这样撤销才能正确追踪。
    2. 重构InsertAtCommand的职责:改名为RemoveAtCommand更贴合功能,execute方法负责移除指定索引的元素并保存,undo方法负责将保存的元素插回原索引。
    3. 确保命令操作的是原列表:创建命令时传入self.list_items,而非副本。
    4. 检查execute_undo中的逻辑:对于RemoveAtCommand(原InsertAtCommand),调用undo方法即可,不需要额外判断混淆逻辑。

内容的提问来源于stack exchange,提问作者user24995660

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 06:46:00