Оптимизация хвостовой рекурсии выглядит следующим образом: в первом примере, поскольку вы не можете вычислить результат умножения return (n / p) * odds(n - 1, p - 1)
, пока не вызовете шансы (n-1), интерператор должен удерживать нашу текущую позицию в памяти (в стеке) , и открыть новый вызов шансов.
Рекурсивно это произойдет и при следующем вызове, и после него, и так далее. Таким образом, к тому времени, когда мы достигнем конца нашей рекурсии и начнем возвращать значения и вычислять продукты, у нас будет n незавершенных операций.
Во втором примере, поскольку выполняемый оператор return просто return odds1(n - 1, p - 1, (n / p) * acc)
, мы можем вычислить аргументы функции и просто вызвать коэффициент 1(n-1) без сохранения текущей позиции. вот где оптимизация, потому что теперь мне не нужно помнить, где я нахожусь каждый раз, когда я открываю новый кадр в стеке.
Думайте об этом как о ссылках на книги. представьте, что вы открываете поваренную книгу и переходите к определенному рецепту, а ингредиенты перечислены следующим образом:
- соль
- ингредиенты на следующей странице
на следующей странице есть
- перец
- ингредиенты на следующей странице
и т.д. как узнать все ингредиенты? вы должны помнить, что вы видели на каждой странице!
хотя второй пример больше похож на следующий список ингредиентов:
- соль
- забудьте об этом, просто используйте то, что вы видите на следующей странице
на следующей странице есть:
- соль
- перец
- забудьте об этом, просто используйте то, что вы видите на следующей странице
и т. д. к тому времени, когда вы дойдете до последней страницы (обратите внимание, что аналогия точна в том, что обе функции вызывают одинаковое количество вызовов), у вас есть все ингредиенты, и вам не нужно «хранить в памяти» то, что вы видели на каждой странице, потому что все это есть на последней странице!
person
davin
schedule
16.01.2011