简体   繁体   English

Python递归深度超出限制,超出限制,并且不知道如何删除递归

[英]Python recursion depth exceeded limit exceeded, and don't know how to delete recursion

Maybe the problem can be solved by deleting all those functions, can't it? 也许可以通过删除所有这些功能来解决问题,不是吗? However, i really don't know what to do to get the source work. 但是,我真的不知道该怎么做才能完成源工作。 By the way, it just simulates a horse in a chesstable, going around and around, randomly, trying to visit each square once; 顺便说一句,它只是在国际象棋桌上模拟一匹马,随机地四处走动,试图访问每个广场一次。 and I get a recursion depth exceeded error. 我得到一个递归深度超过错误。

import random

def main():
    global tries,moves
    tries,moves=0,0
    restart()

def restart():
    global a,indexes,x,y
    a=[[0 for y in range(8)] for x in range(8)] #Costrutto chic
    indexes=[x for x in range(8)]
    #Random part
    x=random.randint(0,7)
    y=random.randint(0,7)
    a[x][y]=1
    start()

def start():
    global i,indexes,moves,tries
    i=0
    random.shuffle(indexes) #List filled with random numbers that i'll use as indexes
    while i<=7:
        if indexes[i]==0:
            move(-2,-1)
        elif indexes[i]==1:
            move(-2,1)
        elif indexes[i]==2:
            move(-1,-2)
        elif indexes[i]==3:
            move(-1,2)
        elif indexes[i]==4:
            move(1,-2)
        elif indexes[i]==5:
            move(1,2)
        elif indexes[i]==6:
            move(2,-1)
        elif indexes[i]==7:
            move(2,1)
        i+=1
    for _ in a:
        if 0 in _:
            print "Wasted moves: %d"%(moves)
            tries+=1
            moves=0
            restart()
    print "Success obtained in %d tries"%(tries)

def move(column,row):
    global x,y,a,moves
    try: b=a[x+row][y+column]
    except IndexError: return 0
    if b==0 and 0<=x+row<=7 and 0<=y+column<=7:
        x=x+row
        y=y+column
        a[x][y]=1
        moves+=1
        start()
    else: return 0

try :main()
#except: print "I couldn't handle it" <-Row added to prevent python from returning a huge amount of errors

EDIT: This is the modified version, which still does not works, but at least it's an improvement: 编辑:这是修改后的版本,它仍然无法正常工作,但至少是一个改进:

import random

def move((column,row),x,y):
    try: cell=squares_visited[x+row][y+column]
    except IndexError: return x,y ## NONE TYPE OBJECT
    if cell==0 and 0<=x+row<=7 and 0<=y+column<=7:
        x+=row
        y+=column
        squares_visited[x][y]=1
        return x,y ## NONE TYPE OBJECT

squares_visited=[[0] * 8 for _ in range(8)]
x=random.randint(0,7)
y=random.randint(0,7)
squares_visited[x][y]=1
moves=[(-2,-1),(-2,1),(-1,-2),(-1,2),(1,-2),(1,2),(2,-1),(2,1)]
indexes=list(range(8))
tries=0
print "The horse starts in position %d,%d"%(x,y)

while True:
    random.shuffle(indexes)
    for _ in indexes:
        cells=move(moves[indexes[_]],x,y) ##Passing as arguments x and y looks weird
        x=cells[0]
        y=cells[1]
    #If you out this for cicle, there are no legal moves anymore(due to full completion with 1, or to lack of legit moves)
    for _ in squares_visited:
        if 0 in _:
            squares_visited=[[0] * 8 for _ in range(8)]
            tries+=1
    else:
        print "You managed to do it in %d tries."%(tries)

This code has a lot of problems -- enough that it's worth going over in full: 这段代码有很多问题-足以值得一遍:

import random

def main():
    global tries,moves

The first of many examples of over-use of global variables. 过度使用全局变量的许多例子中的第一个。 When possible, pass parameters; 如果可能,传递参数; or create a class. 或创建一个类。 This is a general strategy that will help you construct more comprehensible (and thus more debuggable) algorithms; 这是一种通用策略,可帮助您构建更易于理解(从而更易于调试)的算法; and in a general sense, this is part of why your code fails -- not because of any particular bug, but because the complexity of your code makes it hard to find bugs. 从一般意义上讲,这就是代码失败的部分原因-不是因为任何特定的错误,而是因为代码的复杂性使得很难发现错误。

    tries,moves=0,0
    restart()

def restart():
    global a,indexes,x,y

Why do you name your board a ? 为什么你命名你的主板a That's a terrible name! 那是一个糟糕的名字! Use something descriptive like squares_visited . 使用诸如squares_visited描述性squares_visited

    a=[[0 for y in range(8)] for x in range(8)] #Costrutto chic
    indexes=[x for x in range(8)]

Minor: in python 2, [x for x in range(8)] == range(8) -- they do exactly the same thing, so the list comprehension is unnecessary. 次要:在python 2中, [x for x in range(8)] == range(8) -它们做的完全相同,因此不需要列表理解。 In 3, it works a little differently, but if you want a list (rather than a range object) just pass it to list as in ( list(range(8)) ). 在3中,它的工作原理略有不同,但是如果您想要一个列表 (而不是range对象),只需像( list(range(8))list那样将其传递给list

    #Random part
    x=random.randint(0,7)
    y=random.randint(0,7)
    a[x][y]=1
    start()

So my understanding of the code so far is that a is the board, x and y are the starting coordinates, and you've marked the first spot visited with a 1 . 因此,到目前为止,我对代码的理解是, a是木板, xy是起始坐标,并且您已经用1标记了访问的第一个地点。 So far so good. 到现在为止还挺好。 But then things start to get hairy, because you call start at the end of restart instead of calling it from a top-level control function. 但是事情开始变得繁琐,因为您在restart结束时调用了start ,而不是从顶级控制函数中调用它。 That's theoretically OK, but it makes the recursion more complicated than necessary; 从理论上讲这是可以的,但是这会使递归变得不必要的复杂。 this is another part of your problem. 这是您问题的另一部分。

def start():
    global i,indexes,moves,tries

Argh more globals... 更多全球人物...

    i=0
    random.shuffle(indexes) #List filled with random numbers that i'll use as indexes
    while i<=7:
        if indexes[i]==0:
            move(-2,-1)
        elif indexes[i]==1:
            move(-2,1)
        elif indexes[i]==2:
            move(-1,-2)
        elif indexes[i]==3:
            move(-1,2)
        elif indexes[i]==4:
            move(1,-2)
        elif indexes[i]==5:
            move(1,2)
        elif indexes[i]==6:
            move(2,-1)
        elif indexes[i]==7:
            move(2,1)
        i+=1

Ok, so what you're trying to do is go through each index in indexes in sequence. 好了,你正在试图做的是要经过在各指标indexes序列。 Why are you using while though? 你为什么要用while And why is i global?? 为什么i是全球性的? I don't see it being used anywhere else. 我看不到在其他任何地方使用它。 This is way overcomplicated. 这太复杂了。 Just use a for loop to iterate over indexes directly, as in 只需使用for循环即可直接遍历indexes ,如

    for index in indexes: 
        if index==0:
            ...

Ok, now for the specific problems... 好了,现在针对具体问题...

    for _ in a:
        if 0 in _:
            print "Wasted moves: %d"%(moves)
            tries+=1
            moves=0
            restart()
    print "Success obtained in %d tries"%(tries)

I don't understand what you're trying to do here. 我不明白您要在这里做什么。 It seems like you're calling restart every time you find a 0 (ie an unvisited spot) on your board. 似乎您每次在板上找到0 (即未访问的位置)时都在调用restart But restart resets all board values to 0 , so unless there's some other way to fill the board with 1 s before hitting this point, this will result in an infinite recursion. 但是restart会将所有电路板的值重置为0 ,因此,除非有其他方法在达到这一点之前用1 s填充电路板,否则将导致无限递归。 In fact, the mutual recursion between move and start might be able to achieve that in principle, but as it is, it's way too complex! 事实上,之间的相互递归movestart 也许能够实现这一原则,但因为它是,它的方式太复杂了! The problem is that there's no clear recursion termination condition. 问题在于没有明确的递归终止条件。

def move(column,row):
    global x,y,a,moves
    try: b=a[x+row][y+column]
    except IndexError: return 0
    if b==0 and 0<=x+row<=7 and 0<=y+column<=7:
        x=x+row
        y=y+column
        a[x][y]=1
        moves+=1
        start()
    else: return 0

Here, in principle, the idea seems to be that if your move hits a 1 or goes off the board, then the current branch of the recursion terminates. 原则上,这里的想法似乎是,如果您的举动达到1或偏离板,则递归的当前分支终止。 But because i and indexes are global above in start , when start is re-called, it re-shuffles indexes and resets i to 0! 但是因为iindexes以上是在全球start ,当start重新调用,它重新洗牌indexes和重置i比0! The result is sheer chaos! 结果是混乱! I can't even begin to comprehend how that will effect the recursion; 我什至无法理解这将如何影响递归。 it seems likely that because i gets reset at the beginning of start every time, and because every successful call of move results in a call of start , the while loop in start will never terminate! 好像是因为每次i都在start的开始处被重置,并且每次成功调用move导致调用start ,所以start中的while循环永远不会终止!

I suppose it's possible that eventually this process will manage to visit every square, at which point things might work as expected, but as it is, this is too complex even to predict. 我想这个过程最终可能会成功访问每个广场,这时事情可能会按预期进行,但实际上,这甚至太复杂了,甚至无法预测。

try :main()
#except: print "I couldn't handle it" <-Row added to prevent python from returning a huge amount of errors

Not sure what you mean by that last line, but it doesn't sound like a good sign -- you're papering over an error instead of finding the root cause. 不确定最后一行的含义,但这听起来似乎不是一个好兆头-您是在为错误而不是根本原因寻找答案。

I'm going to play with this code a bit and see if I can get it to behave marginally better by de-globalizing some of its state... will report back soon. 我将使用此代码一点,看看是否可以通过取消全局化某些状态来使其表现得更好一些……将尽快报告。

Update : 更新

Ok I de-globalized indexes as described above. 好吧,如上所述,我取消了indexes的全球化。 I then replaced the start / restart recursion with an infinite loop in restart , a return statement in start where the call to restart used to be, and a sys.exit() at the end of start (to break out of the infinite loop on success). 然后我更换了start / restart递归在一个无限循环restart ,一return在声明start ,其中的号召restart曾经是,和sys.exit()在年底start (打出来的无限循环的上成功)。 The result behaves more as expected. 结果表现得比预期的要好。 This is still poor design but it works now, in the sense that it recursively tries a bunch of random paths until every local position has been visited. 这仍然是较差的设计,但从某种意义上说,它递归地尝试了一堆随机路径,直到访问了每个本地位置为止,它现在可以工作。

Of course it still doesn't ever succeed; 当然,它仍然永远不会成功。 it just keeps looping. 它只是不断循环。 Actually finding a solution will probably require a lot more rethinking of this algorithm! 实际上,找到解决方案可能需要对这种算法进行更多的重新思考! But following my above suggestions should help some, at least. 但是遵循我的上述建议至少应该有所帮助。

start() and move() call each other, making it an indirect recursive call BUT the move() return startment get out of move() and notout of the recursion. start()move()互相调用,这使其成为间接递归调用,但move() return起点从move()退出而不是从递归退出。

You see, when you are calling a function, that calls a function that calls a functions, it all goes in a stack that reference each calls. 您会看到,在调用一个函数时,即调用一个调用函数的函数时,所有这些都放在引用每个调用的堆栈中。 When you get a your final result, you are supposed to go backward, and unstack these function calls. 当您获得最终结果时,应该向后退,并拆开这些函数调用的堆栈。

Here, you don't, you call move() , that calls start() , and it it returns something then you just keep going deeper in the stack. 在这里,您没有,您调用了move() ,即调用了start() ,它返回了一些内容,然后您就可以继续深入栈中了。

Try to make an iterative version of your code. 尝试制作代码的迭代版本。 Recursion is hard, start with something easier. 递归很困难,从更轻松的事情开始。

If you do want to persist in the recursive version, make move() call itself, and then go backward in the stack from it self once it reach the out condition. 如果您确实想保留递归版本,请先调用move()本身,然后在达到out条件后从其自身返回堆栈。 It will be clearer than dealing with recursive calls from two functions. 这比处理来自两个函数的递归调用要清晰得多。

BTW: 顺便说一句:

  • Avoid using global variables. 避免使用全局变量。 Either pass the data as arguments, or use a class. 可以将数据作为参数传递,也可以使用一个类。 I would use argument passing, it will force you to come up with a better algo that this one. 我会使用参数传递,它将迫使您提出一个比该算法更好的算法。
  • the while loop is not necessary. while循环不是必需的。 Replace it with a for loop on indexes 用for循环替换索引
  • the huge if/elif statement is not necessary, replace it with a dictionary 不需要巨大的if/elif语句,将其替换为字典

You should end up with somthing like this: 您应该最终得到这样的东西:

for i in indexes:
    move(*MOVES[i])

MOVES , being a dict of values of i associated with params for move() . MOVES ,是与move()参数相关联的i值的指示。

  • you may want to use generators instead of your list comprehensions, but that would require some algo changes. 您可能要使用生成器而不是列表推导,但是这需要进行一些算法上的更改。 It could be better for your memory footprint. 它可能会更好地占用您的内存。 At the very least, make this shorter: 至少将其缩短:

[x for x in range(8)] can be written range(8) [[0 for y in range(8)] for x in range(8)] can be written [[0] * 8 for x in range(8)] [x for x in range(8)]写入range(8) [[0 for y in range(8)] for x in range(8)]可以写入[[0] * 8 for x in range(8)]

range() can be replaced by xrange() range()可以替换为xrange()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM