From 233b0234b77d376378cc8b5715e93d5a38284401 Mon Sep 17 00:00:00 2001 From: Tristan-Raz Date: Thu, 31 Jul 2025 15:59:34 -0400 Subject: [PATCH 1/5] Saving progress for review of draft chapter still needs helper functions --- source/chx-recursion.ptx | 137 +++++++++++++++++++++++++++++++++++++++ source/main.ptx | 1 + 2 files changed, 138 insertions(+) create mode 100644 source/chx-recursion.ptx diff --git a/source/chx-recursion.ptx b/source/chx-recursion.ptx new file mode 100644 index 0000000..fa0ad6e --- /dev/null +++ b/source/chx-recursion.ptx @@ -0,0 +1,137 @@ + + + Recursion in Java: A Transition from Python + +
+ Translating Your Recursive Logic to Java + +

+ As you know from Python, recursion is a powerful problem-solving technique involving base cases and recursive steps. When moving to Java, the core logic you've learned remains identical. The challenge is adapting that logic to Java's statically-typed, class-based syntax. +

+

+ Let's take the familiar factorial function. The logical steps are the same, but the implementation details change. +

+

+ Here is the standard implementation in Python: +

+ + + def factorial(n): + # Base Case: 0! or 1! is 1 + if n <= 1: + return 1 + # Recursive Step: n * (n-1)! + else: + return n * factorial(n - 1) + + print(f"5! is {factorial(5)}") + + +

+ To write this in Java, you must place the function (now called a method) inside a class and declare the data types for its parameters and return value. +

+ + + public class Main { + // A recursive function to calculate the factorial of a number. + public static int factorial(int n) { + // Base case: factorial of 0 or 1 is 1. + if (n <= 1) { + return 1; + } + // Recursive step. + return n * factorial(n - 1); + } + + public static void main(String[] args) { + int n = 3; // Let's calculate the factorial of 3. + int result = Main.factorial(n); // Call the existing factorial method. + System.out.println("The factorial of " + n + " is: " + result); // Output: The factorial of 3 is: 6 + } + } + + +

+ Notice the key differences: instead of def, the method signature public static int declares its visibility, that it belongs to the class rather than an object, and that it returns an int. All logic is contained within curly braces {}. +

+
+
+ +
+ Common Recursive Patterns in Java + +

+ You're likely familiar with how some recursive algorithms, like the naive Fibonacci implementation, are elegant but inefficient due to redundant calculations. In Java, a common and robust pattern to solve this is using a private helper method. This separates the clean, public-facing API from the more complex internal recursion. +

+

+ The following example demonstrates this pattern. The public fib method provides a simple entry point, while the private fibHelper method performs the efficient recursion by carrying its state (the previous two numbers) in its parameters. +

+ + + class Main { + // A standard recursive Fibonacci function + public static int fib(int n) { + if (n <= 0) { // Base case for F(0) + return 0; + } else if (n == 1) { // Base case for F(1) + return 1; + } else { // Recursive step for F(n) = F(n-1) + F(n-2) + return fib(n - 1) + fib(n - 2); + } + } + public static void main(String[] args) { + // Test cases + System.out.println("Fibonacci of 0: " + fib(0)); // Expected: 0 + System.out.println("Fibonacci of 1: " + fib(1)); // Expected: 1 + System.out.println("Fibonacci of 2: " + fib(2)); // Expected: 1 + System.out.println("Fibonacci of 3: " + fib(3)); // Expected: 2 + System.out.println("Fibonacci of 5: " + fib(5)); // Expected: 5 + System.out.println("Fibonacci of 10: " + fib(10)); // Expected: 55 + } + } + + +

+ This public/private helper pattern is a cornerstone of good recursive design in Java, providing both efficiency and a clean interface. +

+
+
+ +
+ Recursion Limits: Python vs. Java +

+ The consequence of deep recursion, running out of stack space, is a concept you've already encountered in Python. Java handles this in a very similar way, throwing an error when the call stack depth is exceeded. +

+

+ The key difference is the name of the error: +

+
    +
  • In Python, this raises a RecursionError.
  • +
  • In Java, this throws a StackOverflowError.
  • +
+

+ Neither language supports tail call optimization tail call optimization, so the practical limits on recursion depth are a factor in both. If an algorithm requires thousands of recursive calls, an iterative (loop-based) approach is the preferred solution in both Python and Java. +

+ + + public class Crash { + + public static void causeStackOverflow() { + // This method calls itself endlessly without a stopping condition (a base case). + // Each call adds a new layer to the program's call stack. + // Eventually, the stack runs out of space, causing the error. + causeStackOverflow(); + } + + // A main method is required to run the program. + public static void main(String[] args) { + System.out.println("Calling the recursive method... this will end in an error!"); + + // This line starts the infinite recursion. + causeStackOverflow(); + } + } + + +
+
\ No newline at end of file diff --git a/source/main.ptx b/source/main.ptx index bddc8f2..c222b57 100644 --- a/source/main.ptx +++ b/source/main.ptx @@ -17,6 +17,7 @@ + From 92dd726a31d00099f46af4455b5ea04f6e41dcb9 Mon Sep 17 00:00:00 2001 From: Jan Pearce Date: Thu, 31 Jul 2025 17:35:16 -0400 Subject: [PATCH 2/5] make Python and Java more parallel --- source/chx-recursion.ptx | 46 +++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/source/chx-recursion.ptx b/source/chx-recursion.ptx index fa0ad6e..3a9d1df 100644 --- a/source/chx-recursion.ptx +++ b/source/chx-recursion.ptx @@ -1,15 +1,20 @@ - Recursion in Java: A Transition from Python + Recursion in Java + + -
- Translating Your Recursive Logic to Java - -

- As you know from Python, recursion is a powerful problem-solving technique involving base cases and recursive steps. When moving to Java, the core logic you've learned remains identical. The challenge is adapting that logic to Java's statically-typed, class-based syntax. +

+ Basic Recursion +

+ In this chapter, we will explore how to translate your recursive logic from Python to Java. While the core concepts of recursion remain the same, the syntax and structure of your code will change somewhat. + +

+

recursion + As you may know from Python, recursion is a powerful problem-solving technique involving base cases and recursive steps in which a function or method calls itself. When moving to Java, the core logic you've learned remains identical. The challenge is adapting that logic to Java's statically-typed, class-based syntax.

- Let's take the familiar factorial function. The logical steps are the same, but the implementation details change. + Let's take the familiar factorial function (which calculates the factorial of a number, namely the product of all positive integers from 1 to n). The logical steps in the code are the same, but the implementation details change.

Here is the standard implementation in Python: @@ -24,13 +29,20 @@ else: return n * factorial(n - 1) - print(f"5! is {factorial(5)}") + # Let's calculate the factorial of 5. + x = 5 + print("The factorial of " + x + " is: " + result)

- To write this in Java, you must place the function (now called a method) inside a class and declare the data types for its parameters and return value. + The Java version follows the same recursive logic but requires three key syntax changes: the method must be inside a class, you must declare the parameter and return types (int n and int return), and you use public static to make it callable from main. The base case and recursive step remain conceptually identical. +

+ +

+ Here is the equivalent Java code:

- + + public class Main { // A recursive function to calculate the factorial of a number. @@ -43,10 +55,12 @@ return n * factorial(n - 1); } + // Let's calculate the factorial of 5. public static void main(String[] args) { - int n = 3; // Let's calculate the factorial of 3. - int result = Main.factorial(n); // Call the existing factorial method. - System.out.println("The factorial of " + n + " is: " + result); // Output: The factorial of 3 is: 6 + int x = 5; + int result = Main.factorial(x); // Call the existing factorial method. + // Print the result. + System.out.println("The factorial of " + x + " is: " + result); } } @@ -54,12 +68,11 @@

Notice the key differences: instead of def, the method signature public static int declares its visibility, that it belongs to the class rather than an object, and that it returns an int. All logic is contained within curly braces {}.

-
- Common Recursive Patterns in Java - + Common Recursive Patterns +

You're likely familiar with how some recursive algorithms, like the naive Fibonacci implementation, are elegant but inefficient due to redundant calculations. In Java, a common and robust pattern to solve this is using a private helper method. This separates the clean, public-facing API from the more complex internal recursion.

@@ -94,7 +107,6 @@

This public/private helper pattern is a cornerstone of good recursive design in Java, providing both efficiency and a clean interface.

-
From 345c5d0917e1938bf399b3e522c93c5922bea176 Mon Sep 17 00:00:00 2001 From: Tristan-Raz Date: Fri, 1 Aug 2025 11:51:20 -0400 Subject: [PATCH 3/5] Fixed broken codes and added class based python --- source/chx-recursion.ptx | 76 +++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/source/chx-recursion.ptx b/source/chx-recursion.ptx index 3a9d1df..dab6bab 100644 --- a/source/chx-recursion.ptx +++ b/source/chx-recursion.ptx @@ -3,12 +3,10 @@ Recursion in Java -
Basic Recursion

In this chapter, we will explore how to translate your recursive logic from Python to Java. While the core concepts of recursion remain the same, the syntax and structure of your code will change somewhat. -

recursion As you may know from Python, recursion is a powerful problem-solving technique involving base cases and recursive steps in which a function or method calls itself. When moving to Java, the core logic you've learned remains identical. The challenge is adapting that logic to Java's statically-typed, class-based syntax. @@ -21,60 +19,68 @@

- def factorial(n): - # Base Case: 0! or 1! is 1 - if n <= 1: - return 1 - # Recursive Step: n * (n-1)! - else: - return n * factorial(n - 1) - - # Let's calculate the factorial of 5. - x = 5 - print("The factorial of " + x + " is: " + result) - + class MathTools: + """A utility class for mathematical operations.""" + def factorial(n: int) -> int: + """Calculates the factorial of n using recursion.""" + # A check for negative numbers is good practice. + if n < 0: + raise ValueError("Factorial is not defined for negative numbers.") # Base Case: 0! or 1! is 1 + if n <= 1: + return 1 # Recursive Step: n * (n-1)! + # The call is now to the method within the class. + return n * MathTools.factorial(n - 1)# This block shows how to use the class method. + if __name__ == "__main__": + number = 5 + result = MathTools.factorial(number) # Call the method on the class + print(f"{number}! is {result}") +

The Java version follows the same recursive logic but requires three key syntax changes: the method must be inside a class, you must declare the parameter and return types (int n and int return), and you use public static to make it callable from main. The base case and recursive step remain conceptually identical.

-

Here is the equivalent Java code:

- - public class Main { - // A recursive function to calculate the factorial of a number. + public class MathTools { /** + * Calculates the factorial of n using recursion. + * This is a static method, like Python's @staticmethod. + * @param n The non-negative integer. + * @return The factorial of n as a long to prevent overflow for larger numbers. + */ public static int factorial(int n) { - // Base case: factorial of 0 or 1 is 1. + // A check for negative numbers is good practice. + if (n < 0) { + throw new IllegalArgumentException("Factorial is not defined for negative numbers."); + } // Base Case: 0! or 1! is 1 if (n <= 1) { return 1; - } - // Recursive step. + } // Recursive Step: n * (n-1)! return n * factorial(n - 1); - } - - // Let's calculate the factorial of 5. + } /** + * The main entry point for the application. + * This is the Java equivalent of Python's 'if __name__ == "__main__":' + */ public static void main(String[] args) { - int x = 5; - int result = Main.factorial(x); // Call the existing factorial method. - // Print the result. - System.out.println("The factorial of " + x + " is: " + result); + int number = 5; + // The static method is called directly on the class. + long result = MathTools.factorial(number); System.out.println(number + "! is " + result); } }

- Notice the key differences: instead of def, the method signature public static int declares its visibility, that it belongs to the class rather than an object, and that it returns an int. All logic is contained within curly braces {}. + Notice the key differences: instead of def, the method signature public static int declares its scope, that it belongs to the class rather than an object, and that it returns an int. All logic is contained within curly braces {}.

-
Common Recursive Patterns -

- You're likely familiar with how some recursive algorithms, like the naive Fibonacci implementation, are elegant but inefficient due to redundant calculations. In Java, a common and robust pattern to solve this is using a private helper method. This separates the clean, public-facing API from the more complex internal recursion. + You're likely familiar with how some recursive algorithms, like the naive Fibonacci implementation, + are elegant but inefficient, due to branching recursive calls filling the call stack. A common and robust pattern to solve + this is using a private helper method. This separates the clean, public-facing API from the more complex internal recursion.

The following example demonstrates this pattern. The public fib method provides a simple entry point, while the private fibHelper method performs the efficient recursion by carrying its state (the previous two numbers) in its parameters. @@ -102,13 +108,12 @@ System.out.println("Fibonacci of 10: " + fib(10)); // Expected: 55 } } - +

This public/private helper pattern is a cornerstone of good recursive design in Java, providing both efficiency and a clean interface.

-
Recursion Limits: Python vs. Java

@@ -127,18 +132,15 @@ public class Crash { - public static void causeStackOverflow() { // This method calls itself endlessly without a stopping condition (a base case). // Each call adds a new layer to the program's call stack. // Eventually, the stack runs out of space, causing the error. causeStackOverflow(); } - // A main method is required to run the program. public static void main(String[] args) { System.out.println("Calling the recursive method... this will end in an error!"); - // This line starts the infinite recursion. causeStackOverflow(); } From 8838b22719ba89283c311b4c036283d00497b341 Mon Sep 17 00:00:00 2001 From: Tristan-Raz Date: Fri, 1 Aug 2025 14:22:00 -0400 Subject: [PATCH 4/5] Adds better explanation and better helper methods --- source/chx-recursion.ptx | 70 +++++++++++++++++++++++++++------------- 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/source/chx-recursion.ptx b/source/chx-recursion.ptx index dab6bab..c905517 100644 --- a/source/chx-recursion.ptx +++ b/source/chx-recursion.ptx @@ -77,42 +77,68 @@

Common Recursive Patterns -

- You're likely familiar with how some recursive algorithms, like the naive Fibonacci implementation, - are elegant but inefficient, due to branching recursive calls filling the call stack. A common and robust pattern to solve - this is using a private helper method. This separates the clean, public-facing API from the more complex internal recursion. + +

+ In many recursive algorithms, the recursive calls need extra information that the original caller shouldn't have to provide. For example, to recursively process an array, you need to keep track of the current position (index). To traverse a tree, you need to know the current node. This extra information clutters the public-facing method signature. +

+

+ A common pattern to solve this is using a private helper method. This pattern lets you create a clean, simple public method that users will call, while the private helper method handles the complex details of the recursion. The public method typically makes the initial call to the private helper, providing the necessary starting values for the extra parameters. +

+

+ Let's see this pattern in action with an example that calculates the sum of all elements in an integer array. The public sum method only takes the array, but the private sumHelper method also takes an index to track its progress through the array. +

+ +

+ You're likely familiar with how some recursive algorithms, like the naive Fibonacci implementation, + are elegant but inefficient, due to branching recursive calls filling the call stack. A common pattern to solve + this is using a private helper method.

The following example demonstrates this pattern. The public fib method provides a simple entry point, while the private fibHelper method performs the efficient recursion by carrying its state (the previous two numbers) in its parameters.

- class Main { - // A standard recursive Fibonacci function - public static int fib(int n) { - if (n <= 0) { // Base case for F(0) - return 0; - } else if (n == 1) { // Base case for F(1) - return 1; - } else { // Recursive step for F(n) = F(n-1) + F(n-2) - return fib(n - 1) + fib(n - 2); + public class FibonacciExample { + public int fib(int n) { + if (n < 0) { + throw new IllegalArgumentException("Input cannot be negative."); + } + // Initial call to the recursive helper with depth 0. + return this._fibHelper(n, 0, 1, 0); + } + private int _fibHelper(int count, int a, int b, int depth) { + // Create an indent string based on the recursion depth. + String indent = " ".repeat(depth); + // Print when the method is entered (pushed onto the stack). + System.out.printf("%s[>>] ENTERING _fibHelper(count=%d, a=%d, b=%d)%n", indent, count, a, b); + // Base Case: When the count reaches 0, 'a' holds the result. + if (count == 0) { + System.out.printf("%s[<<] EXITING (Base Case) -> returns %d%n", indent, a); + return a; } + // Recursive Step. + int result = this._fibHelper(count - 1, b, a + b, depth + 1); + // Print when the method exits (popped from the stack). + System.out.printf("%s[<<] EXITING (Recursive Step) -> passing %d%n", indent, result); + return result; } public static void main(String[] args) { - // Test cases - System.out.println("Fibonacci of 0: " + fib(0)); // Expected: 0 - System.out.println("Fibonacci of 1: " + fib(1)); // Expected: 1 - System.out.println("Fibonacci of 2: " + fib(2)); // Expected: 1 - System.out.println("Fibonacci of 3: " + fib(3)); // Expected: 2 - System.out.println("Fibonacci of 5: " + fib(5)); // Expected: 5 - System.out.println("Fibonacci of 10: " + fib(10)); // Expected: 55 + FibonacciExample calculator = new FibonacciExample(); + int n = 4; // Let's calculate the 4th Fibonacci number. + System.out.printf("--- Calculating fib(%d) ---%n", n); + int result = calculator.fib(n); + System.out.println("--------------------------"); + System.out.printf("The %dth Fibonacci number is: %d%n", n, result); } }

- This public/private helper pattern is a cornerstone of good recursive design in Java, providing both efficiency and a clean interface. -

+ This helper method approach is significantly more efficient in terms of time than the classic branching recursion (where fib(n) calls fib(n-1) and fib(n-2)). The branching model has an exponential time complexity of roughly O(2^n) because it re-calculates the same values many times. In contrast, our helper method has a linear time complexity of O(n), as it avoids re-computation by carrying the previous two results (a and b) forward into the next call. +

+

+ However, regarding memory efficiency, the comparison is different. The maximum depth of the call stack for both the naive and the helper method is proportional to n, giving them both a space complexity of O(n). This means that while the helper method is much faster, it is equally vulnerable to a StackOverflowError for very large values of n. Because Java does not perform tail-call optimization, any recursive solution that goes too deep will exhaust the stack memory, regardless of its time efficiency. For true memory efficiency (O(1) space), an iterative loop-based solution is superior. +

Recursion Limits: Python vs. Java From 5a8e305c8fdc4ad3ed453b6b0bf8d7f779c60374 Mon Sep 17 00:00:00 2001 From: Tristan-Raz Date: Fri, 1 Aug 2025 15:57:36 -0400 Subject: [PATCH 5/5] Adds parallel python code --- source/chx-recursion.ptx | 75 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/source/chx-recursion.ptx b/source/chx-recursion.ptx index c905517..4304a89 100644 --- a/source/chx-recursion.ptx +++ b/source/chx-recursion.ptx @@ -96,6 +96,10 @@

The following example demonstrates this pattern. The public fib method provides a simple entry point, while the private fibHelper method performs the efficient recursion by carrying its state (the previous two numbers) in its parameters.

+

+ The following Java code demonstrates a similar pattern. +

+ public class FibonacciExample { @@ -139,6 +143,51 @@

However, regarding memory efficiency, the comparison is different. The maximum depth of the call stack for both the naive and the helper method is proportional to n, giving them both a space complexity of O(n). This means that while the helper method is much faster, it is equally vulnerable to a StackOverflowError for very large values of n. Because Java does not perform tail-call optimization, any recursive solution that goes too deep will exhaust the stack memory, regardless of its time efficiency. For true memory efficiency (O(1) space), an iterative loop-based solution is superior.

+

+ The following Python code demonstrates the same pattern, using a public method to initiate the calculation and a private helper method to perform the recursion. +

+ + + class FibonacciExample: + def fib(self, n: int) -> int: + """ + Public method to start the Fibonacci calculation. + """ + if n < 0: + raise ValueError("Input cannot be negative.") + # Initial call to the recursive helper with depth 0. + return self._fib_helper(n, 0, 1, 0) + + def _fib_helper(self, count: int, a: int, b: int, depth: int) -> int: + """ + Private helper that performs the tail recursion to find the number. + """ + # Create an indent string based on the recursion depth. + indent = " " * depth + # Print when the method is entered (pushed onto the stack). + print(f"{indent}[>>] ENTERING _fib_helper(count={count}, a={a}, b={b})") + + # Base Case: When the count reaches 0, 'a' holds the result. + if count == 0: + print(f"{indent}[<<] EXITING (Base Case) -> returns {a}") + return a + + # Recursive Step. + result = self._fib_helper(count - 1, b, a + b, depth + 1) + # Print when the method exits (popped from the stack). + print(f"{indent}[<<] EXITING (Recursive Step) -> passing {result}") + return result + + # The standard Python entry point, equivalent to Java's `main` method. + if __name__ == "__main__": + calculator = FibonacciExample() + n = 4 # Let's calculate the 4th Fibonacci number. + print(f"--- Calculating fib({n}) ---") + result = calculator.fib(n) + print("--------------------------") + print(f"The {n}th Fibonacci number is: {result}") + +
Recursion Limits: Python vs. Java @@ -155,7 +204,31 @@

Neither language supports tail call optimization tail call optimization, so the practical limits on recursion depth are a factor in both. If an algorithm requires thousands of recursive calls, an iterative (loop-based) approach is the preferred solution in both Python and Java.

- +

+ The following Python code demonstrates a situation where a function calls itself indefinitely without a base case, leading to aRecursionError. +

+ + + def cause_recursion_error(): + """ + This function calls itself without a base case, guaranteeing an error. + """ + cause_recursion_error() + + # Standard Python entry point + if __name__ == "__main__": + print("Calling the recursive function... this will end in an error!") + + # This line starts the infinite recursion. + # Python will stop it and raise a RecursionError automatically. + cause_recursion_error() + + + +

+ The following Java code demonstrates a similar situation, where a method calls itself indefinitely without a base case, leading to a StackOverflowError. +

+ public class Crash { public static void causeStackOverflow() {