Путаница в рекурсии python

#python #python-2.7 #list #recursion

#python #python-2.7 #Список #рекурсия

Вопрос:

Я писал рекурсивную функцию для решения проблемы, в которой, по сути, вы пытаетесь найти все комбинации для достижения целевой суммы с заданным количеством чисел, и каждое число меньше определенного предела.

Написанный мной код выглядит следующим образом:

 results = []

def findSchedulesHelper(workHours_left, dayHoursLimit, days_left, res):
    if workHours_left < 0:
        return
    elif workHours_left > 0 and days_left == 0:
        return
    elif workHours_left == 0 and days_left != 0:
        for i in range(days_left):
            res.append(0)
        results.append(res)
        return
    elif days_left == 0 and workHours_left == 0:
        results.append(res)
        return
    else:
        for i in range(dayHoursLimit   1):
            res.append(i)
            findSchedulesHelper(workHours_left - i, dayHoursLimit, days_left - 1, res)
            res.pop()


def findSchedules(workHours, dayHours, pattern):
    # Write your code here
    num_question_mark = 0
    total_hours_so_far = 0
    for char in pattern:
        if char == "?":
            num_question_mark  = 1
        else:
            total_hours_so_far  = int(char)

    hours_left_to_fill = workHours - total_hours_so_far

    for i in range(dayHours   1):
        findSchedulesHelper(hours_left_to_fill - i, dayHours, num_question_mark - 1, [i])
    print results

findSchedules(3,2,"??2??00")
  

но это дает неправильный ответ.

Если я изменю одну строку и сделаю ее:

 results = []

def findSchedulesHelper(workHours_left, dayHoursLimit, days_left, res):
    if workHours_left < 0:
        return
    elif workHours_left > 0 and days_left == 0:
        return
    elif workHours_left == 0 and days_left != 0:
        for i in range(days_left):
            res.append(0)
        results.append(res)
        return
    elif days_left == 0 and workHours_left == 0:
        results.append(res)
        return
    else:
        for i in range(dayHoursLimit   1):
            findSchedulesHelper(workHours_left - i, dayHoursLimit, days_left - 1, res   [i])


def findSchedules(workHours, dayHours, pattern):
    # Write your code here
    num_question_mark = 0
    total_hours_so_far = 0
    for char in pattern:
        if char == "?":
            num_question_mark  = 1
        else:
            total_hours_so_far  = int(char)

    hours_left_to_fill = workHours - total_hours_so_far

    for i in range(dayHours   1):
        findSchedulesHelper(hours_left_to_fill - i, dayHours, num_question_mark - 1, [i])
    print results

findSchedules(3,2,"??2??00")
  

Тогда все правильно. Я действительно не понимаю, почему это так. Я попытался выполнить некоторую отладку и обнаружил, что append и pop работают не так, как я ожидал от них. Было бы здорово, если бы кто-нибудь мог объяснить, в чем именно разница между двумя способами, которыми я выполнил функцию.

Комментарии:

1. Я должен выполнить поиск по 40 строкам кода, чтобы найти ваше изменение? en.wikipedia.org/wiki/Where’s_Wally?

Ответ №1:

Первая программа добавляет элемент в конец res , выполняет рекурсивный вызов findSchedulesHelper() и после вызова удаляет добавленный элемент:

 res.append(i)
findSchedulesHelper(workHours_left - i, dayHoursLimit, days_left - 1, res)
res.pop()
  

В то время как вторая программа передает новый список, в котором есть все, что есть res плюс дополнительный элемент. Его не нужно очищать после рекурсивного вызова:

 findSchedulesHelper(workHours_left - i, dayHoursLimit, days_left - 1, res   [i])
  

Одно из различий между ними связано с этим кодом:

 for i in range(days_left):
    res.append(0)
  

То есть рекурсивный вызов к findSchedulesHelper() может измениться res . Если это произойдет, res.pop() при возврате может удаляться что-то другое, чем вы думаете. И в конце res следующего вызова могут быть дополнительные данные. Вторая программа не сталкивается с этой проблемой, поскольку она передает копию res и никогда больше не просматривает и не использует копию повторно.

Следующими потенциальными проблемами являются множественные вызовы:

 results.append(res)
  

В первой программе вы передаете один и тот же res список, поэтому results в итоге получается несколько указателей на res , все из которых являются одним и тем же точным списком, всегда отражающим его текущее состояние, а не его состояние на момент добавления в results . Во второй программе вы передаете копию, res поэтому все записи в results отличаются от состояния, в res котором она была добавлена results .