为什么这个递归函数不会破坏?

时间:2013-11-21 02:24:09

标签: python

简短版本:

# -*- coding: UTF-8 -*-

import re
import itertools

targ = ['\t- Task 09', '\tThis is a comment', '\t\t- Subtask 9.02', '\t\t\t- Subsubtask 9.03', '\t\t\t\t- Subsubsubtask 9.01 @done', '\t\t\t\t- Subsubsubtask 9.02', '\t\t\t\t\t- Subsubsubsubtask 9.01 @done', '\t\t\t\t- Subsubsubtask 9.03 @done', '\t\t\t- Subsubtask 9.04', '\t\t\t\t- 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', '\t\t- Subtask 9.02', '\t\t\t- Subsubtask 9.03', '\t\t\t\t- Subsubsubtask 9.01 @done', '\t\t\t\t- Subsubsubtask 9.02', '\t\t\t\t\t- Subsubsubsubtask 9.01 @done', '\t\t\t\t- Subsubsubtask 9.03 @done', '\t\t\t- Subsubtask 9.04', '\t\t\t\t- 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',则意味着下一个任务任务的子任务。如果 task 的所有子项都标有“@done”,则还应标记 task 。我过滤整个输入以避免注释(根据语法),这会破坏代码。我还保留原始列表中的索引,然后添加'@done'标记。我希望这段代码能够顺利运行给大家。感谢您的光临和阅读这段文字(:


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

# -*- coding: UTF-8 -*-

import re
import itertools

targ = ['\t- Task 09', '\tThis is a comment', '\t\t- Subtask 9.02', '\t\t\t- Subsubtask 9.03', '\t\t\t\t- Subsubsubtask 9.01 @done', '\t\t\t\t- Subsubsubtask 9.02', '\t\t\t\t\t- Subsubsubsubtask 9.01 @done', '\t\t\t\t- Subsubsubtask 9.03 @done', '\t\t\t- Subsubtask 9.04', '\t\t\t\t- 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 09\n\tThis is a comment\n\t\t- Subtask 9.01 @done\n\t\tThis is a subtask comment\n\t\t- Subtask 9.02\n\t\tAnother comment\n\t\t\t- Subsubtask 9.01 @done\n\t\t\t- Subsubtask 9.02 @done\n\t\t\tWhy so many comments?\n\t\t\t- Subsubtask 9.03\n\t\t\t\t- Subsubsubtask 9.01 @done\n\t\t\t\tJust another comment to break your code\n\t\t\t\t- Subsubsubtask 9.02\n\t\t\t\tBreak, break, break\n\t\t\t\t\t- Subsubsubsubtask 9.01 @done\n\t\t\t\t\tThis is the last comment, I promise\n\t\t\t\t- Subsubsubtask 9.03 @done\n\t\t\t\tI lied.\n\t\t\t- This is the task of interest\n\t\t\t\t- Subsubsubtask 9.03 @done\n\t\t\t\tI love lying\n\t\t\t- Subsubtask 9.05 @done\n\t\t- Subtask 9.03 @done\n\t\tOk, this is truly the last comment.\n\t\tNah.'

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 %s\n" %(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 yet\n---------------------\n"

    return l

for task in regex:
    itask = task.split('\n')
    DoneChild2Parent(itask)

该字符串是使用TaskPaper语法的任务列表,TaskPaper是Mac的任务管理器,使用纯文本文件。任务以' - '开头,项目以':'结尾(在此示例中不存在),评论既不是。缩进定义任务和注释之间的层次结构。当任务标记为已完成时,它会获得一个标记'@done'(每个标记都以@开头)。对于这个例子,我使用的是单个多层次的任务。如果有多个主要任务(当项目是直接父项时),那么最终的正则表达式和for循环是存在的。

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

当你运行这个脚本时,你会发现这个目的是有效的,但是循环的进行比预期更多并且采取了一个奇怪的方向。我将问题发生的任务命名为“这是感兴趣的任务”,因此您可以在打印时轻松识别它。

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

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

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

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

我正在寻找的结果是第6轮第8轮。感谢您的关注,我很抱歉这么长的文字。

2 个答案:

答案 0 :(得分:1)

我首先要问你希望这个正则表达式匹配的是什么:

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

用言语解释你的意图?例如,您是否对此字符串匹配感到惊讶:

"abc @done- def"

?您是否意识到该正则表达式中的尾随.*没有任何意义(正则表达式与是否匹配相同的字符串)?

我不知道这是否与你的问题有关;-)但是我的生活中已经看到了很多正则表达式,我无法猜出你希望这个会做什么。我最好的猜测是,你希望它匹配\t*-\s并不紧跟@done之前 - 但在这种情况下,你需要在*断言之后看到负面看法。

答案 1 :(得分:1)

感谢您的帮助。 This StackOverflow thread给了我很多帮助。我只需要在正确的位置放置一个break

# -*- coding: UTF-8 -*-

import re
import itertools

targ = ['\t- Task 09', '\tThis is a comment', '\t\t- Subtask 9.02', '\t\t\t- Subsubtask 9.03', '\t\t\t\t- Subsubsubtask 9.01 @done', '\t\t\t\t- Subsubsubtask 9.02', '\t\t\t\t\t- Subsubsubsubtask 9.01 @done', '\t\t\t\t- Subsubsubtask 9.03 @done', '\t\t\t- Subsubtask 9.04', '\t\t\t\t- 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)

为什么会这样?很简单,循环会调用函数,但它不会结束。因此,当最后一个循环调用no函数并结束时,它会将结果返回到前一个函数并继续循环,这将发现先前任务中已修复的其他问题并再次调用该函数,生成您看到的混乱(: