为什么在递归调用之后执行此语句?

时间:2019-05-04 03:06:31

标签: python python-3.x recursion

我必须编写代码来模拟两个战士之间的一对一对偶。为此,我编写了一个类Warriors,其属性为healthattack

决斗是根据以下原则进行的:

每个回合中的一个战士都会碰到另一个,这将使他失去健康,其价值与第一个战士的攻击相同。之后,第二个战士将对第一个战士做同样的事情。

为实现上述逻辑,我编写了以下名为lets_dual的递归函数。

class Warrior:
    def __init__(self, x, y):
        self.health = x
        self.attack = y
        self.is_alive = True


def lets_dual(warrior1, warrior2):

    print('warrior1 health', warrior1.health)
    print('warrior2 health', warrior2.health)

    if warrior2.health > 1 and warrior1.health > 1:
        # print('after attack warrior1 health', warrior1.health)
        warrior2.health -= warrior1.attack
        print('after attack warrior2 health', warrior2.health)

        # check if warrior 2 is still alive or not
        if warrior2.health > 0:
            warrior1.health -= warrior2.attack
        print('after attack warrior1 health', warrior1.health)

        lets_dual(warrior1, warrior2)

        print('this wont get printed')
    elif warrior1.health > 0:
        warrior2.is_alive = False
    else:
        warrior1.is_alive = False


dave = Warrior(50, 5)
carl = Warrior(50, 7)
print(lets_dual(dave, carl))
print(dave.is_alive)

问题是递归逻辑不能正常工作。语句'this wont get printed'正以某种方式被执行。另外,我想添加一个显示warrior2.is_alive flag的return语句,但它返回的是None值。

3 个答案:

答案 0 :(得分:1)

我不确定为什么一开始甚至需要递归,您可以轻松地将if warrior2.health > 1 and warrior1.health > 1:替换为while循环,以检查同一件事while warrior2.health > 1 and warrior1.health > 1:

在while循环中反复运行逻辑要比通过if条件使用递归循环更好。

还请注意,您无需从lets_dual函数返回任何内容,因为您担心warrior.health在运行该函数后可以访问,而lets_dual函数只是为您设置is_alive属性!

所以当我切换到while循环

class Warrior:
    def __init__(self, x, y):
        self.health = x
        self.attack = y
        self.is_alive = True


def lets_dual(warrior1, warrior2):

    print('warrior1 health', warrior1.health)
    print('warrior2 health', warrior2.health)

    #Replace if with a while
    while warrior2.health > 1 and warrior1.health > 1:
        # print('after attack warrior1 health', warrior1.health)
        warrior2.health -= warrior1.attack
        print('after attack warrior2 health', warrior2.health)

        # check if warrior 2 is still alive or not
        if warrior2.health > 0:
            warrior1.health -= warrior2.attack
        print('after attack warrior1 health', warrior1.health)

    if warrior1.health > 0:
        warrior2.is_alive = False
    else:
        warrior1.is_alive = False

dave = Warrior(50, 5)
carl = Warrior(50, 7)
lets_dual(dave, carl)
print(dave.is_alive)
print(carl.is_alive)

输出结果为

warrior1 health 50
warrior2 health 50
after attack warrior2 health 45
after attack warrior1 health 43
after attack warrior2 health 40
after attack warrior1 health 36
after attack warrior2 health 35
after attack warrior1 health 29
after attack warrior2 health 30
after attack warrior1 health 22
after attack warrior2 health 25
after attack warrior1 health 15
after attack warrior2 health 20
after attack warrior1 health 8
after attack warrior2 health 15
after attack warrior1 health 1
True
False

答案 1 :(得分:1)

与任何函数调用一样,递归调用完成后会将控制权返回给调用函数。将其与调用print进行比较,在print完成将文本打印到屏幕上之后,代码继续进行。

因此,如果递归后还有更多代码,则在调用完成后它将最终运行。如果多余的代码显示了有关调用的任何内容,则您可能会看到调用堆栈的“展开”,最后一个调用者首先运行该代码,然后是调用者这样做,然后是其调用者,依此类推。考虑一下此函数,该函数将打印从0到您传入的参数的数字,并使用此展开将它们按正确的顺序排列:

def print_range(n):
    if n > 0:
        print_range(n-1)  # this recursive call doesn't end the function
    print(n)              # so this always runs, in both the base and recursive cases

如果您希望递归结束该函数,则要么需要将其放在末尾,而后没有代码,要么应使用return语句从任何位置停止。如果您的递归函数返回一个值,则通常需要从递归调用中返回该值:

def my_sum(sequence, total=0):
    if sequence:
        return my_sum(sequence[1:], total+sequence[0])  # this time we return here
    return total  # so this only runs in the base case (when the sequence is empty)

虽然对于初学者来说了解递归很重要,但是值得注意的是,当您使用循环来控制重复动作而不是递归时,Python通常会更快。某些算法仍然最容易通过递归实现,但是当您可以轻松地翻译某些递归代码以使用循环时,您可能应该这样做。

答案 2 :(得分:0)

您需要的是适当的终止条件

class Warrior:
    def __init__(self, x, y):
        self.health = x
        self.attack = y
        self.is_alive = True


def lets_dual(warrior1, warrior2):

    print('warrior1 health', warrior1.health)
    print('warrior2 health', warrior2.health)

    # Termination condition
    if warrior2.health < 1:
        warrior2.is_alive = False
        print ("warrior2 is dead. Let's return")
        return

    # Termination condition
    if warrior1.health < 1:
        warrior1.is_alive = False
        print ("warrior1 is dead. Let's return")
        return

    # Else let's battle
    warrior2.health -= warrior1.attack
    print('after attack warrior2 health', warrior2.health)

    # check if warrior 2 is still alive or not
    if warrior2.health > 0:
        warrior1.health -= warrior2.attack
    print('after attack warrior1 health', warrior1.health)

    lets_dual(warrior1, warrior2)


dave = Warrior(50, 5)
carl = Warrior(50, 7)
print(lets_dual(dave, carl)) # This will print None as we return None from lets_dual
print(dave.is_alive)