From ac9519280bad703d06679bc5d6a87c647e278f0e Mon Sep 17 00:00:00 2001 From: BethanyG Date: Tue, 26 May 2026 15:40:40 -0700 Subject: [PATCH 1/7] Fixed typos and formatting for recursion concept. --- concepts/recursion/about.md | 70 ++++++++++++++++++------------ concepts/recursion/introduction.md | 8 +++- 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/concepts/recursion/about.md b/concepts/recursion/about.md index 1cf24388269..c9f798898af 100644 --- a/concepts/recursion/about.md +++ b/concepts/recursion/about.md @@ -1,9 +1,10 @@ # About Recursion is a way to repeatedly execute code inside a function through the function calling itself. -Functions that call themselves are know as _recursive_ functions. +Functions that call themselves are known as _recursive_ functions. Recursion can be viewed as another way to loop/iterate. -And like looping, a Boolean expression or `True/False` test is used to know when to stop the recursive execution. +And like looping, a Boolean expression or `True / False` test is used to determine when to stop the recursive execution. + _Unlike_ looping, recursion without termination in Python cannot not run infinitely. Values used in each function call are placed in their own frame on the Python interpreter stack. If the total number of function calls takes up more space than the stack has room for, it will result in an error. @@ -12,7 +13,7 @@ If the total number of function calls takes up more space than the stack has roo Looping and recursion may _feel_ similar in that they are both iterative. However, they _look_ different, both at the code level and at the implementation level. -Looping can take place within the same frame on the call stack. +Looping can take place within the same frame on the [call stack][what-is-the-call-stack]. This is usually managed by updating one or more variable values to progressively maintain state for each iteration. This is an efficient implementation, but it can be somewhat cluttered when looking at the code. @@ -26,6 +27,7 @@ If there is risk of causing a stack error or overflow, why would anyone use a re _Readability, traceability, and intent._ There may be situations where a solution is more readable and/or easier to reason through when expressed through recursion than when expressed through looping. There may also be program constraints with using/mutating data, managing complexity, delegating responsibility, or organizing workloads. + Problems that lend themselves to recursion include complex but repetitive problems that grow smaller over time, particularly [divide and conquer][divide and conquer] algorithms and [cumulative][cumulative] algorithms. However, due to Python's limit for how many frames are allowed on the stack, not all problems will benefit from a fully recursive strategy. Problems less naturally suited to recursion include ones that have a steady state, but need to repeat for a certain number of cycles, problems that need to execute asynchronously, and situations calling for a great number of iterations. @@ -45,18 +47,22 @@ Finally, Adya decides that the function needs a parameter for _which weekday_ of For all these requirements, she decides to use the `date` class imported from `datetime`. Putting all of that together, Adya comes up with: -``` +```python from datetime import date def paydates_for_year(year, weekday, ordinal): """Returns a list of the matching weekday dates. - Keyword arguments: - year -- the year, e.g. 2022 - weekday -- the weekday, e.g. 3 (for Wednesday) - ordinal -- which weekday of the month, e.g. 2 (for the second) + Arguments: + year (int): The year (e.g. 2022)/ + weekday (int): The weekday number (e.g. 3 for Wednesday). + ordinal (int): Which weekday of the month (e.g. 2 for the second day). + + Returns: + output (list): Matching weekday dates. """ + output = [] for month in range(1, 13): @@ -70,40 +76,45 @@ def paydates_for_year(year, weekday, ordinal): print(paydates_for_year(2022, 3, 2)) ``` -This first iteration works, but Adya wonders if she can refactor the code to use fewer lines with less nested looping. +This first iteration works, but Adya wonders if she can refactor the code to use fewer lines and less nested looping. She's also read that it is good to minimize mutating state, so she'd like to see if she can avoid mutating some of her variables such as `output`, `month`, and `day_num` . -She's read about recursion, and thinks about how she might change her program to use a recursive approach. +She also knows about recursion, and thinks about how she might change her program to use a recursive approach. The variables that are created and mutated in her looping function could be passed in as arguments instead. Rather than mutating the variables _inside_ her function, she could pass _updated values as arguments_ to the next function call. With those intentions she arrives at this recursive approach: -``` +```python from datetime import date - def paydates_for_year_rec(year, weekday, ordinal, month, day_num, output): """Returns a list of the matching weekday dates - Keyword arguments: - year -- the year, e.g. 2022 - weekday -- the weekday, e.g. 3 (for Wednesday) - ordinal -- which weekday of the month, e.g. 2 (for the second) - month -- the month currently being processed - day_num -- the day of the month currently being processed - output -- the list to be returned + Arguments: + year (int): The year (e.g. 2022)/ + weekday (int): The weekday number (e.g. 3 for Wednesday). + ordinal (int): Which weekday of the month (e.g. 2 for the second day). + month (int): The month number currently being processed. + day_num (int): The day number of the month currently being processed. + + Returns: + output (list): Matching weekday dates. """ + if month == 13: return output + if date(year, month, day_num).isoweekday() == weekday: - return paydates_for_year_rec(year, weekday, ordinal, month + 1, 1, output - + [date(year, month, day_num + (ordinal - 1) * 7)]) + return paydates_for_year_rec( + year, weekday, ordinal, month + 1, 1, output + + [date(year, month, day_num + (ordinal - 1) * 7)] + ) + return paydates_for_year_rec(year, weekday, ordinal, month, day_num + 1, output) - # find the second Wednesday of the month for all the months in 2022 - print(paydates_for_year_rec(2022, 3, 2, 1, 1, [])) - +# find the second Wednesday of the month for all the months in 2022 +print(paydates_for_year_rec(2022, 3, 2, 1, 1, [])) ``` Adya is happy that there are no more nested loops, no mutated state, and 2 fewer lines of code! @@ -118,10 +129,11 @@ After working out the basic logic, she can then focus on optimizing her initial Even later, when she learns about `tuples`, Adya could consider further "optimizing" approaches, such as using a `list comprehension` with `Calendar.itermonthdates`, or memoizing certain values. + ## Recursive Variation: The Tail Call A tail call is when the last statement of a function only calls itself and nothing more. -This example is not a tail call, as the function adds 1 to the result of calling itself +This example is not a tail call, as the function adds 1 to the result of calling itself: ```python def print_increment(step, max_value): @@ -140,7 +152,7 @@ if __name__ == "__main__": ``` -This will print +This will print: ``` The step is 1 @@ -148,7 +160,7 @@ The step is 2 retval is 3 after recursion ``` -To refactor it to a tail call, make `retval` a parameter of `print_increment` +To refactor it to a tail call, make `retval` a parameter of `print_increment`. ```python def print_increment(step, max_value, retval): @@ -172,7 +184,7 @@ However, it is always important when using recursion to know that there will not ## Recursion Limits in Python -Some languages are able to optimize tail calls so that each recursive call reuses the stack frame of the first call to the function (_similar to the way a loop reuses a frame_), instead of adding an additional frame to the stack. +Some languages are able to optimize tail calls so that each recursive call reuses the [stack frame][stack-frame] of the first call to the function (_similar to the way a loop reuses a frame_), instead of adding an additional frame to the stack. Python is not one of those languages. To guard against stack overflow, Python has a recursion limit that defaults to one thousand frames. A [RecursionError](https://docs.python.org/3.8/library/exceptions.html#RecursionError) exception is raised when the interpreter detects that the recursion limit has been exceeded. @@ -192,3 +204,5 @@ To learn more about using recursion in Python you can start with [setrecursionlimit]: https://docs.python.org/3.8/library/sys.html#sys.setrecursionlimit [divide and conquer]: https://afteracademy.com/blog/divide-and-conquer-approach-in-programming [cumulative]: https://www.geeksforgeeks.org/sum-of-natural-numbers-using-recursion/ +[stack-frame]: https://shanechang.com/p/python-frames-systems-programming-connection/ +[what-is-the-call-stack]: https://en.wikipedia.org/wiki/Call_stack diff --git a/concepts/recursion/introduction.md b/concepts/recursion/introduction.md index fb7e1970705..a073a5698c5 100644 --- a/concepts/recursion/introduction.md +++ b/concepts/recursion/introduction.md @@ -2,9 +2,9 @@ Recursion is a way to repeat code in a function by the function calling itself. It can be viewed as another way to loop/iterate. -Like looping, a Boolean expression or `True/False` test is used to know when to stop the recursive execution. +Like looping, a Boolean expression or `True / False` test is used to know when to stop the recursive execution. _Unlike_ looping, recursion without termination in Python cannot not run infinitely. -Values used in each function call are placed in their own frame on the Python interpreter stack. +Values used in each function call are placed in their own [frame][stack-frame] on the Python [interpreter stack][what-is-the-call-stack]. If the total number of function calls takes up more space than the stack has room for, it will result in an error. ```python @@ -33,3 +33,7 @@ After recursion ``` There may be some situations that are more readable and/or easier to reason through when expressed through recursion than when expressed through looping. + + +[stack-frame]: https://shanechang.com/p/python-frames-systems-programming-connection/ +[what-is-the-call-stack]: https://en.wikipedia.org/wiki/Call_stack \ No newline at end of file From c56086dd7001524cf1c52a9e882ab79ecaa089d3 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Thu, 28 May 2026 10:38:54 -0700 Subject: [PATCH 2/7] Update concepts/recursion/about.md Co-authored-by: Yrahcaz <74512479+Yrahcaz7@users.noreply.github.com> --- concepts/recursion/about.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concepts/recursion/about.md b/concepts/recursion/about.md index c9f798898af..fc5deff50a0 100644 --- a/concepts/recursion/about.md +++ b/concepts/recursion/about.md @@ -3,7 +3,7 @@ Recursion is a way to repeatedly execute code inside a function through the function calling itself. Functions that call themselves are known as _recursive_ functions. Recursion can be viewed as another way to loop/iterate. -And like looping, a Boolean expression or `True / False` test is used to determine when to stop the recursive execution. +And like looping, a Boolean expression or `True`/`False` test is used to determine when to stop the recursive execution. _Unlike_ looping, recursion without termination in Python cannot not run infinitely. Values used in each function call are placed in their own frame on the Python interpreter stack. From 2434c9b654da42ad617d3b810bdcf96341b4294e Mon Sep 17 00:00:00 2001 From: BethanyG Date: Thu, 28 May 2026 10:39:12 -0700 Subject: [PATCH 3/7] Update concepts/recursion/about.md Co-authored-by: Yrahcaz <74512479+Yrahcaz7@users.noreply.github.com> --- concepts/recursion/about.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concepts/recursion/about.md b/concepts/recursion/about.md index fc5deff50a0..fbf23f665f3 100644 --- a/concepts/recursion/about.md +++ b/concepts/recursion/about.md @@ -92,7 +92,7 @@ def paydates_for_year_rec(year, weekday, ordinal, month, day_num, output): """Returns a list of the matching weekday dates Arguments: - year (int): The year (e.g. 2022)/ + year (int): The year (e.g. 2022). weekday (int): The weekday number (e.g. 3 for Wednesday). ordinal (int): Which weekday of the month (e.g. 2 for the second day). month (int): The month number currently being processed. From 206a56213a9203959b41b0fed1467fcb15a09028 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Thu, 28 May 2026 10:40:10 -0700 Subject: [PATCH 4/7] Update concepts/recursion/introduction.md Co-authored-by: Yrahcaz <74512479+Yrahcaz7@users.noreply.github.com> --- concepts/recursion/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concepts/recursion/introduction.md b/concepts/recursion/introduction.md index a073a5698c5..1775d79995e 100644 --- a/concepts/recursion/introduction.md +++ b/concepts/recursion/introduction.md @@ -2,7 +2,7 @@ Recursion is a way to repeat code in a function by the function calling itself. It can be viewed as another way to loop/iterate. -Like looping, a Boolean expression or `True / False` test is used to know when to stop the recursive execution. +Like looping, a Boolean expression or `True`/`False` test is used to determine when to stop the recursive execution. _Unlike_ looping, recursion without termination in Python cannot not run infinitely. Values used in each function call are placed in their own [frame][stack-frame] on the Python [interpreter stack][what-is-the-call-stack]. If the total number of function calls takes up more space than the stack has room for, it will result in an error. From 72562354c13251ceb61ed5ac958824e57c903bc6 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Thu, 28 May 2026 11:05:00 -0700 Subject: [PATCH 5/7] Applied changes from code review. --- concepts/recursion/about.md | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/concepts/recursion/about.md b/concepts/recursion/about.md index fbf23f665f3..4efa054bdde 100644 --- a/concepts/recursion/about.md +++ b/concepts/recursion/about.md @@ -18,7 +18,7 @@ This is usually managed by updating one or more variable values to progressively This is an efficient implementation, but it can be somewhat cluttered when looking at the code. Recursion, rather than updating _variable state_, can pass _updated values_ directly as arguments to the next call (iteration) of the same function. -This declutters the body of the function and can clarify how each update happens. +This de-clutters the body of the function and can clarify how each update happens. However, it is also a less efficient implementation, as each call to the same function adds another frame to the stack. ## Recursion: Why and Why Not? @@ -55,7 +55,7 @@ def paydates_for_year(year, weekday, ordinal): """Returns a list of the matching weekday dates. Arguments: - year (int): The year (e.g. 2022)/ + year (int): The year (e.g. 2022). weekday (int): The weekday number (e.g. 3 for Wednesday). ordinal (int): Which weekday of the month (e.g. 2 for the second day). @@ -121,13 +121,13 @@ Adya is happy that there are no more nested loops, no mutated state, and 2 fewer She is a little concerned that the recursive approach uses more steps than the looping approach, and so is less "performant". But re-writing the problem using recursion has definitely helped her deal with ugly nested looping (_a performance hazard_), extensive state mutation, and confusion around complex conditional logic. -It also feels more "readable" - she is sure that when she comes back to this code after a break, she will be able to read through and remember what it does more easily. +It also feels more "readable" — she is sure that when she comes back to this code after a break, she will be able to read through and remember what it does more easily. In the future, Adya may try to work through problems recursively first. She may find it easier to initially walk through the problem in clear steps when nesting, mutation, and complexity are minimized. After working out the basic logic, she can then focus on optimizing her initial recursive steps into a more performant looping approach. -Even later, when she learns about `tuples`, Adya could consider further "optimizing" approaches, such as using a `list comprehension` with `Calendar.itermonthdates`, or memoizing certain values. +Even later, when she learns about [concept:python/tuples](), Adya could consider further "optimizing" approaches, such as using a [`list comprehension`][list-comprehension] with [`Calendar.itermonthdates`][itermonthssates], or [memoizing][memoization] certain values. ## Recursive Variation: The Tail Call @@ -187,8 +187,8 @@ However, it is always important when using recursion to know that there will not Some languages are able to optimize tail calls so that each recursive call reuses the [stack frame][stack-frame] of the first call to the function (_similar to the way a loop reuses a frame_), instead of adding an additional frame to the stack. Python is not one of those languages. To guard against stack overflow, Python has a recursion limit that defaults to one thousand frames. -A [RecursionError](https://docs.python.org/3.8/library/exceptions.html#RecursionError) exception is raised when the interpreter detects that the recursion limit has been exceeded. -It is possible to use the [sys.setrecursionlimit](https://docs.python.org/3.8/library/sys.html#sys.setrecursionlimit) method to increase the recursion limit, but doing so runs the risk of having a runtime segmentation fault that will crash the program, and possibly the operating system. +A [RecursionError][RecursionError] exception is raised when the interpreter detects that the recursion limit has been exceeded. +It is possible to use the [sys.setrecursionlimit][sys.setrecursionlimit] method to increase the recursion limit, but doing so runs the risk of having a runtime segmentation fault that will crash the program, and possibly the operating system. ## Resources @@ -197,12 +197,16 @@ To learn more about using recursion in Python you can start with - [Real Python: python-recursion][Real Python: python-recursion] - [Real Python: python-thinking-recursively][Real Python: python-thinking-recursively] -[python-programming: recursion]: https://www.programiz.com/python-programming/recursion + [Real Python: python-recursion]: https://realpython.com/python-recursion/ [Real Python: python-thinking-recursively]: https://realpython.com/python-thinking-recursively/ [RecursionError]: https://docs.python.org/3.8/library/exceptions.html#RecursionError -[setrecursionlimit]: https://docs.python.org/3.8/library/sys.html#sys.setrecursionlimit -[divide and conquer]: https://afteracademy.com/blog/divide-and-conquer-approach-in-programming [cumulative]: https://www.geeksforgeeks.org/sum-of-natural-numbers-using-recursion/ +[divide and conquer]: https://afteracademy.com/blog/divide-and-conquer-approach-in-programming +[itermonthssates]: https://docs.python.org/3/library/calendar.html#calendar.Calendar.itermonthdates +[list-comprehension]: https://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/ +[memoization]: https://dbader.org/blog/python-memoization +[python-programming: recursion]: https://www.programiz.com/python-programming/recursion [stack-frame]: https://shanechang.com/p/python-frames-systems-programming-connection/ +[sys.setrecursionlimit]: https://docs.python.org/3.8/library/sys.html#sys.setrecursionlimit [what-is-the-call-stack]: https://en.wikipedia.org/wiki/Call_stack From ff3ab0f1202d419fe58132703441612d7c281366 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Thu, 28 May 2026 12:49:43 -0700 Subject: [PATCH 6/7] Update concepts/recursion/about.md Co-authored-by: Yrahcaz <74512479+Yrahcaz7@users.noreply.github.com> --- concepts/recursion/about.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concepts/recursion/about.md b/concepts/recursion/about.md index 4efa054bdde..a01e3585dc6 100644 --- a/concepts/recursion/about.md +++ b/concepts/recursion/about.md @@ -127,7 +127,7 @@ In the future, Adya may try to work through problems recursively first. She may find it easier to initially walk through the problem in clear steps when nesting, mutation, and complexity are minimized. After working out the basic logic, she can then focus on optimizing her initial recursive steps into a more performant looping approach. -Even later, when she learns about [concept:python/tuples](), Adya could consider further "optimizing" approaches, such as using a [`list comprehension`][list-comprehension] with [`Calendar.itermonthdates`][itermonthssates], or [memoizing][memoization] certain values. +Even later, when she learns about [concept:python/tuples](), Adya could consider further "optimizing" approaches, such as using a [`list comprehension`][list-comprehension] with [`Calendar.itermonthdates`][itermonthdates], or [memoizing][memoization] certain values. ## Recursive Variation: The Tail Call From ff060e242688797464178797561a1d786207e136 Mon Sep 17 00:00:00 2001 From: BethanyG Date: Thu, 28 May 2026 12:50:08 -0700 Subject: [PATCH 7/7] Update concepts/recursion/about.md Co-authored-by: Yrahcaz <74512479+Yrahcaz7@users.noreply.github.com> --- concepts/recursion/about.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/concepts/recursion/about.md b/concepts/recursion/about.md index a01e3585dc6..3d11c7ca270 100644 --- a/concepts/recursion/about.md +++ b/concepts/recursion/about.md @@ -203,7 +203,7 @@ To learn more about using recursion in Python you can start with [RecursionError]: https://docs.python.org/3.8/library/exceptions.html#RecursionError [cumulative]: https://www.geeksforgeeks.org/sum-of-natural-numbers-using-recursion/ [divide and conquer]: https://afteracademy.com/blog/divide-and-conquer-approach-in-programming -[itermonthssates]: https://docs.python.org/3/library/calendar.html#calendar.Calendar.itermonthdates +[itermonthdates]: https://docs.python.org/3/library/calendar.html#calendar.Calendar.itermonthdates [list-comprehension]: https://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/ [memoization]: https://dbader.org/blog/python-memoization [python-programming: recursion]: https://www.programiz.com/python-programming/recursion