Задача рекурсии 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.