为什么这个递归函数不中断?

  • 本文关键字:中断 递归函数 python
  • 更新时间 :
  • 英文 :


简短版本:

# -*- coding: UTF-8 -*-
import re
import itertools
targ = ['t- Task 09', 'tThis is a comment', 'tt- Subtask 9.02', 'ttt- Subsubtask 9.03', 'tttt- Subsubsubtask 9.01 @done', 'tttt- Subsubsubtask 9.02', 'ttttt- Subsubsubsubtask 9.01 @done', 'tttt- Subsubsubtask 9.03 @done', 'ttt- Subsubtask 9.04', 'tttt- Subsubsubtask 9.19 @done']

def DoneChild2Parent(l):
    tasks = [(k,v) for k,v in enumerate(l) if re.search('t+-s.*',v)]
    for x,y in tasks:       
        if (x,y) == tasks[len(tasks[1:])]:
            print (x,y), '==', tasks[len(tasks[1:])]
            return l #Also tried break here.
        elif re.search('(?!.*@done)t*-s.*', y):
            next_task = tasks[tasks.index((x,y))+1]
            if next_task[1].count('t') > y.count('t'):
                subtasks = list(itertools.takewhile(lambda t: t.count('t') > y.count('t'), [z for w,z in tasks[tasks.index(next_task):]]))
                if all('@done' in subtask for subtask in subtasks):
                    l[x]+=' @done'
                    DoneChild2Parent(l)
    # return l #when using break above
print DoneChild2Parent(targ)

此函数应在匹配列表的最新元素时返回。现在它一直在循环。它确定何时(x,y) == tasks[len(tasks[1:])]:,但返回和中断都不会使我退出循环。


评论后:

这是一个更精简的代码版本。我尽量减少争论,同时保留问题:

# -*- coding: UTF-8 -*-
import re
targ = ['t- Task 09', 'tThis is a comment', 'tt- Subtask 9.02', 'ttt- Subsubtask 9.03', 'tttt- Subsubsubtask 9.01 @done', 'tttt- Subsubsubtask 9.02', 'ttttt- Subsubsubsubtask 9.01 @done', 'tttt- Subsubsubtask 9.03 @done', 'ttt- Subsubtask 9.04', 'tttt- Subsubsubtask 9.19 @done']

def DoneChild2Parent(l):
    tasks = iter([(x,y) for x,y in enumerate(l) if re.search('t+-s.*',y)])
    for k in tasks:
        if re.search('(?!.*@done)t*-s.*', k[1]):
            nt = tasks.next()
            if nt[1].count('t') > k[1].count('t'):
                subtasks = [subtask for subtask in l[k[0]:] if subtask.count('t') > k[1].count('t') and re.search('t+-s.*',subtask) ]
                if all('@done' in subtask for subtask in subtasks):
                    l[k[0]]+=' @done'
                    DoneChild2Parent(l)
    return l
print DoneChild2Parent(targ)

关于@abarnert的问题:我需要";下一个任务";以比较它与有问题的任务之间的"\t"的数量。如果下一个任务的'\t'多于任务一个任务是的子任务。如果任务的所有子级都用"@完成";它还应该标记任务。我过滤整个输入以避免注释(根据语法),这会破坏代码。我还保留了原始列表中的索引,以便在之后添加"@done"标记。我希望这段代码对你们所有人来说运行得更顺利。感谢您路过并阅读这面文字墙(:


具有相同问题的不同版本的代码:

# -*- coding: UTF-8 -*-
import re
import itertools
targ = ['t- Task 09', 'tThis is a comment', 'tt- Subtask 9.02', 'ttt- Subsubtask 9.03', 'tttt- Subsubsubtask 9.01 @done', 'tttt- Subsubsubtask 9.02', 'ttttt- Subsubsubsubtask 9.01 @done', 'tttt- Subsubsubtask 9.03 @done', 'ttt- Subsubtask 9.04', 'tttt- Subsubsubtask 9.19 @done']

def DoneChild2Parent(l):
    tasks = [(k,v) for k,v in enumerate(l) if re.search('t+-s.*',v)]
    for x,y in tasks[:-1]:
        if re.search('(?!.*@done)t*-s.*', y):
            next_task = tasks[tasks.index((x,y))+1]
            if next_task[1].count('t') > y.count('t'):
                subtasks = list(itertools.takewhile(lambda t: t.count('t') > y.count('t'), [z for w,z in tasks[tasks.index(next_task):]]))
                if all('@done' in subtask for subtask in subtasks):
                    l[x]+=' @done'
                    DoneChild2Parent(l)
    return l
print DoneChild2Parent(targ)

用原始代码进行了很长的解释:

为公开的标题道歉,最重要的是要求你找出问题,而不是只建议解决已经找到的问题。下面的代码是我正在使用的代码的简化版本,我删除了一些无用的函数,但结果是一样的。控制台上打印的结果将引导我们解决我的问题:

# -*- coding: UTF-8 -*-
import re
import itertools
targ = 't- Task 09ntThis is a commentntt- Subtask 9.01 @donenttThis is a subtask commentntt- Subtask 9.02nttAnother commentnttt- Subsubtask 9.01 @donenttt- Subsubtask 9.02 @donentttWhy so many comments?nttt- Subsubtask 9.03ntttt- Subsubsubtask 9.01 @donenttttJust another comment to break your codentttt- Subsubsubtask 9.02nttttBreak, break, breaknttttt- Subsubsubsubtask 9.01 @donentttttThis is the last comment, I promisentttt- Subsubsubtask 9.03 @donenttttI lied.nttt- This is the task of interestntttt- Subsubsubtask 9.03 @donenttttI love lyingnttt- Subsubtask 9.05 @donentt- Subtask 9.03 @donenttOk, this is truly the last comment.nttNah.'
regex =  re.split('n(?=^t-s)', targ, 0, re.M)
c = 0
def DoneChild2Parent(l):
    global c
    c+=1
    tasks = iter([(x,y) for x,y in enumerate(l) if re.search('t+-s.*',y)])
    
    print "=================n==:: ROUND %s ::==n================="%(c)
    
    for k in tasks:
        print "* Step 1: %s => This is the task being checked:" %(str(k))
        if re.search('(?!.*@done)t*-s.*', k[1]):
            print "* Step 2: %s => Was not done so we check the next task/subtasks" %(str(k))
            nt = tasks.next()
            if nt[1].count('t') > k[1].count('t'):
                print "* Step 3: %s => is the next subtask" %(str(nt))
                subtasks = [subtask for subtask in l[k[0]:] if subtask.count('t') > k[1].count('t') and re.search('t+-s.*',subtask) ]
                print "* Length of subtasks is",len(subtasks)
                print "* Step 4: Subtasks for %sn" %(k[0]),subtasks
                if all('@done' in subtask for subtask in subtasks):
                    print "n* Step 5: Adding @done to %s at index %s" %(k[1].strip(), k[0])
                    print "Before: ",l[k[0]]
                    l[k[0]]+=' @done'
                    print "After: ",l[k[0]],"n---------------------n"
                    DoneChild2Parent(l)
                else:
                    print "n* Step 5: Not everything is done yetn---------------------n"
                    
    return l
for task in regex:
    itask = task.split('n')
    DoneChild2Parent(itask)

该字符串是一个使用TaskPaper语法的任务列表,TaskPaper是Mac的任务管理器,使用纯文本文件。任务以"-"开头,项目以":"结尾(本例中没有),注释两者都不是。缩进定义任务和注释之间的层次结构。当任务被标记为完成时,它会获得一个标记"@done"(每个标记都以@开头)。在这个例子中,我使用了一个单一的、多层次的任务。如果有多个主要任务(当项目是直接父任务时),regex和最后的for循环就会出现。

该函数循环执行列表中的每个任务,检查该任务是否已撤消以及是否包含子任务。然后,它收集所有子任务,并检查它们是否都标记为@done,如果是,它也将父任务标记为@doe。

当你运行这个脚本时,你会发现目的是有效的,但循环的进行超出了预期,并采取了一个奇怪的方向。我将出现问题的任务命名为"这是感兴趣的任务",这样你就可以很容易地在打印中发现它。

在脚本的第3轮中,感兴趣的任务被正确标记为@done。脚本继续循环,直到它在第5轮中标记主任务,这意味着每个子任务都被标记为@done(列表是考虑到这一点而构建的)。现在,请好好看看第六轮。它应该是最新的循环,函数会检查是否没有什么可做的,并返回值,然而,正如您可能从以下行中注意到的那样:

* Step 1: (22, 'tt- Subtask 9.03 @done') => This is the task being checked:
* Step 1: (4, 'tt- Subtask 9.02 @done') => This is the task being checked:

它检查最后一个任务并循环回到开始。我不知道为什么它会在第二个任务中循环。这种行为重复发生,并且开始重新循环的任务总是不同的。我想知道为什么会这样。但最糟糕的是,您会注意到,在给定的时刻,脚本找到了"感兴趣的任务",即我们之前标记为@done的任务,撤消并再次操作循环。结果是,"感兴趣的任务"最终附带了3@done标签:

After:              - This is the task of interest @done @done @done 

我期待的结果是第8轮在第6轮。谢谢你的关注,很抱歉发了这么长的短信。

我首先要问您到底希望这个正则表达式匹配什么:

if re.search('(?!.*@done)t*-s.*', k[1]):

用语言解释你的意图?例如,你对它匹配以下字符串感到惊讶吗:

"abc @done- def"

您是否意识到该正则表达式中尾随的.*没有任何作用(正则表达式匹配的字符串完全相同,无论是否匹配)?

我不知道这是否与你的问题有关;-),但我在生活中见过很多regexp,我猜不出你希望这个regexp会做什么。我的最佳猜测是,你希望它与t*-s匹配,而不是紧接着@done,但在这种情况下,你希望使用否定的"背后"断言。

感谢您的帮助。这个StackOverflow线程帮了我很多忙。我只需要把break放在正确的位置:

# -*- coding: UTF-8 -*-
import re
import itertools
targ = ['t- Task 09', 'tThis is a comment', 'tt- Subtask 9.02', 'ttt- Subsubtask 9.03', 'tttt- Subsubsubtask 9.01 @done', 'tttt- Subsubsubtask 9.02', 'ttttt- Subsubsubsubtask 9.01 @done', 'tttt- Subsubsubtask 9.03 @done', 'ttt- Subsubtask 9.04', 'tttt- Subsubsubtask 9.19 @done']

def DoneChild2Parent(l):
    tasks = [(k,v) for k,v in enumerate(l) if re.search('t+-s.*',v)]
    for x,y in tasks:       
        if re.search('(?!.*@done)t*-s.*', y):
            next_task = tasks[tasks.index((x,y))+1]
            if next_task[1].count('t') > y.count('t'):
                subtasks = list(itertools.takewhile(lambda t: t.count('t') > y.count('t'), [z for w,z in tasks[tasks.index(next_task):]]))
                if all('@done' in subtask for subtask in subtasks):
                    l[x]+=' @done'
                    DoneChild2Parent(l)
                    break
    return l
print DoneChild2Parent(targ)

为什么会发生这种情况?很简单,循环会调用函数,但不会结束。因此,当最后一个循环没有调用任何函数并结束时,它会将结果返回到上一个函数并继续循环,循环会发现以前任务中已经解决的其他问题,并再次调用该函数,从而生成您看到的混乱(:

最新更新