Задача рекурсии Python
В настоящее время я вхожу в введение в Python и класс теории вычислений, и в последнее время на сложном вопросе был сложный вопрос, который я просто не смог решить. Это связано с написанием кода для программы, которая добавляет числа. Я считаю, что этот вопрос должен использовать рекурсию. Я не помню, как именно был сформулирован вопрос, но вот основная идея.
Внедрить функцию multiadder(n)
, которая принимает неотрицательное целое число n
и добавляет n
произвольные значения вместе. Каждое добавленное значение должно быть записано как отдельный вызов. Например:
>>> multi_three = multiadder(3)
>>> multi_three(1)(2)(3)
6
>>> multiadder(5)(1)(2)(3)(4)(5)
15
Код должен быть записан путем заполнения пробелов.
def multiadder(n):
assert n > 0
if _________________________ :
return _________________________
else:
return _________________________
Темы, которые мы рассмотрели в классе, - это функции более высокого порядка, рекурсия, лямбда и контрольные утверждения. Нам не разрешено использовать структуру данных, такую как списки и наборы, и нам не разрешено ничего импортировать.
Кто-то, пожалуйста, помогите. Это единственная проблема, с которой я не мог пройти тест!
Ответы
Ответ 1
Попробуйте следующее:
def multiadder(n):
assert n > 0
if n == 1:
return lambda t: t
else:
return lambda a: lambda b: multiadder(n-1)(a+b)
if __name__ == '__main__':
print(multiadder(5)(1)(2)(3)(4)(5))
Для n == 1
результат должен быть функцией, возвращающей вход.
В n > 1
завершите результат n-1
, добавив ввод.
Это также работает для конкатенации строк и других операций аккумулирования:
>>> multiadder(3)('p')('q')('r')
pqr
Ответ 2
Вы можете сделать это так, но это почти нечитаемо. Я надеюсь, что объяснения будут полезными:
def multiadder(n):
assert n > 0
if n == 0:
return 0
else:
return (lambda f: lambda i, n, sm: f(f, i, n, sm))(
lambda rec, i, n, sm: sm+i if n == 0 else lambda j: rec(rec, j, n-1, sm+i)
)(0, n, 0)
Посмотрите, как он работает на repl.it.
Как это работает
Возвращаемое значение состоит из трех основных частей:
(lambda f: lambda i, n, sm: f(f, i, n, sm))
Короче говоря, эта функция назначает имя функции, поэтому ее можно вызывать рекурсивно. Более детально:
Он принимает функцию f
, которая сама должна принять 4 аргумента, первая из которых должна быть самореференцией.
Возвращаемая здесь функция принимает три других аргумента и возвращает рекурсивный вызов f
.
Вторая часть - это реальное ядро:
(lambda rec, i, n, sm: sm+i if n == 0 else lambda j: rec(rec, j, n-1, sm+i))
Это передается как аргумент первой функции выше, что делает рекурсию возможной.
Эта функция принимает 4 аргумента, упомянутых выше, и применяет конкретную логику:
-
i
- это число, которое необходимо добавить
-
n
- количество ожидаемых значений после этого
-
sm
- накопленная сумма (исключая i
)
В зависимости от того, ожидаются ли другие значения, эта функция возвращает окончательный результат (sm+i
) или
функция с одним аргументом, которая будет делать то же, что описано здесь (рекурсия), с уменьшением n
и адаптированной суммой.
Наконец, начальные значения передаются выше:
(0, n, 0)
Значение, начинаем с ожидающих значений 0 (фиктивный), n
и текущей суммы 0.
Примечание
Поскольку рекурсия в приведенном выше коде не включает вызов multiladder
, и это утверждение действительно исключает условие if
, чтобы быть истинным, мы можем обойтись без этого if...else
:
def multiadder(n):
assert n > 0
return (lambda f: lambda i, n, sm: f(f, i, n, sm))(
lambda rec, i, n, sm: sm+i if n == 0 else lambda j: rec(rec, j, n-1, sm+i)
)(0, n, 0)
Ответ 3
Вы также можете определить внутреннюю вспомогательную функцию (loop
), которая отслеживает состояние суммы (acc
), поскольку состояние счетчика (n
) уменьшает
def multiadder(n):
def loop(acc,n):
if n == 0:
return acc
else:
return lambda x: loop(acc+x, n-1)
return loop(0,n)
print(multiadder(3)(1)(2)(3)) # 6
print(multiadder(5)(1)(2)(3)(4)(5)) # 15
Это не так элегантно, как DarkKnight, но для начинающего может быть проще концептуализировать. Фактически, этот шаблон настолько полезен и универсален, что я использую его для определения почти всех моих рекурсивных процедур.
def some_recursive_func:
def loop(...state_parameters):
if base_condition:
return answer
else:
return loop(...updated_state_variables)
return loop(...initial_state_variables)
Единственное, что мы применили к этому шаблону, - это обертка рекурсивной ветки выражения if
в lambda x: ...
- это потому, что multiadd
предназначен для возврата функций до тех пор, пока не будут возвращены возвращаемые функции n
.
Еще один, просто для удовольствия, используя легендарный Y-combinator. Это, вероятно, не соответствует критериям, предоставленным вашим инструктором, но, тем не менее, это здорово. Многострочные lambdas в python на самом деле не вещь, поэтому читаемость немного страдает.
U = lambda f: f(f)
Y = U (lambda h: lambda f: f (lambda x: h (h) (f) (x)))
multiadder = Y (lambda f: lambda acc: lambda n:
acc if n == 0 else lambda x: f (acc+x) (n-1)
) (0)
print(multiadder(3)(1)(2)(3)) # 6
print(multiadder(5)(1)(2)(3)(4)(5)) # 15
Или вы все равно могли бы определить multiadder
с помощью синтаксиса def
, если хотите
def multiadder(n):
return Y (lambda f: lambda acc: lambda n:
acc if n == 0 else lambda x: f (acc+x) (n-1)
) (0) (n)
Он работает одинаково.
Я настоятельно рекомендую вам проследить оценку шаг за шагом, чтобы понять, как это работает. В понимании этих функций более высокого порядка есть некоторые огромные идеи.
Ответ 4
Поначалу казалось, что многим нужно было два входа для работы, но ответ довольно прост. Я бы не подумал, что для класса "Введение в Python" нужны функции lamba и т.д.
Это окончательный ответ:
def multiadder(n):
assert n > 0
if n == 1:
return 1
else:
return n + multiadder(n - 1)
На следующем рисунке объясняется, как это работает для n = 4:
multiadder(4)
|
return 4 + multiadder(3)
|
return 3 + multiadder(2)
|
return 2 + multiadder(1)
|
return 1
Таким образом, конечный результат для n = 4 заканчивается 4 + 3 + 2 + 1 = 10.