Writing a recursive function by using optional parameters

Click For Summary
The discussion revolves around two approaches to writing a recursive function for finding the largest palindrome in a string, highlighting the use of optional parameters. The first method employs an optional parameter to handle recursive calls, while the second method separates the public interface from the recursive logic, promoting clearer structure and encapsulation. Key points include the advantages and disadvantages of each approach. The first method is noted for being more concise but criticized for potentially violating object-oriented principles by exposing a public call that could be misused. It also introduces a conditional check that could impact performance in more complex scenarios. The second method is favored for its clarity, efficiency, and separation of concerns, allowing for better validation and control over the recursive process. Additionally, the use of a nullable integer for optional parameters is suggested as a more modern and clear alternative to using sentinel values like -1. Overall, the preference leans towards the second method for its cleaner and more maintainable code structure.
SlurrerOfSpeech
Messages
141
Reaction score
11
One way that I often write recursive functions in my work is by having an optional parameter that is used for all calls after the first. For example, here's how I might implement the classic "Find the largest palindrome in a given string" interview-type problem:

Code:
using System;

public static class Extensions
{
    private static string Reverse(this string s)
    {
        char[] charArray = s.ToCharArray();
        Array.Reverse(charArray);
        return new string(charArray);
    }   
   
    public static string LargestPalindrome(this string s, int k = -1)
    {
        if(k == -1) // If function was called for the first time
        {
            return s.LargestPalindrome(s.Length);           
        }

        // Try to find palindrome in all substrings of length k
        for(int i = 0, j = s.Length - k; i <= j; ++i)
        {
            string ss = s.Substring(i, k);
            if(ss == ss.Reverse())
                return ss;           
        }
       
        // If here, didn't find a palindrome. Try all substrings
        // of length k - 1.
        return s.LargestPalindrome(k - 1);
       
    }
}

                   
public class Program
{   
    public static void Main()
    {
        string s = "kandasndddsk"; 
        Console.WriteLine(s.LargestPalindrome()); // Expected output: "ddd" 
    }
}

This is an alternative to writing it like

Code:
using System;

public static class Extensions
{
    private static string Reverse(this string s)
    {
        char[] charArray = s.ToCharArray();
        Array.Reverse(charArray);
        return new string(charArray);
    }   
   
    private static string LargetsPalindrome_Subroutine(this string s, int k)
    {
        // Try to find palindrome in all substrings of length k
        for(int i = 0, j = s.Length - k; i <= j; ++i)
        {
            string ss = s.Substring(i, k);
            if(ss == ss.Reverse())
                return ss;           
        }
       
        // If here, didn't find a palindrome. Try all substrings
        // of length k - 1.
        return s.LargestPalindrome(k - 1);       
    }
   
    public static string LargestPalindrome(this string s)
    {
        return s.LargestPalindrome(s.Length);                   
    }
}

                   
public class Program
{   
    public static void Main()
    {
        string s = "kandasndddsk"; 
        Console.WriteLine(s.LargestPalindrome()); // Expected output: "ddd" 
    }
}

Let me explain what I think are the advantages and disadvantages of the first way.

Advantages:
* Fancier
* Slightly less code

Disadvantages:
* Although its intent is for the caller to use it with empty pamarameters, the call with a parameter is public and so it can be used as not intended. It could be argued that this violates OOP religious commandements.
 
Technology news on Phys.org
Personally I would in general fancy the second version and possibly with the recursive function non-public as you have made it unless it really was meant to be called by an unknown caller. In your case the recursive function is simple and it may make sense for a caller to specify the maximum palindrome length, but in other cases where delicate data structures have to be prepared first you would not want to leave that up to an unknown caller.

Also, in this particular case (and since its already on tail recursive form) I think I would write the algorithm as a single non-recursive function, possibly still with an optional argument where the caller can specify maximum palindrome length.

Later: I suppose this is just an example and you are aware that the comparison with the reverse string can be done without allocating a new string.
 
I would disagree with both of the claimed Advantages.

1. "Fancier". This is simply not an advantage. In what way is "fancy" code better? If anything, "fancier" code can lead to bugs as usage of it can be obscure (as implied in the Disadvantages).

2. "Slightly less code". While the first version does away with one method definition, the equivalent call is still made, and the (doesn't always mean much) measure "lines of code" is about the same.

Worse, an entire conditional is added - that has to be evaluated in every iteration. The "if (k == -1)" is tested every time the recursed method is called ... and the condition it looks for only applies once. It might not matter in this simple example, but in general a recursed or iterated method should be as lean as possible.

So I'd prefer the second method. It's cleaner, clearer and more efficient.

Even if I wanted callers to be able to specify the maximum length of palindrome, I'd have the public and private parts separated. The public method would be able to do validation (like checking the requested length isn't longer than the string itself), before calling the private method to do the work.

Finally, and maybe this is more an issue of style, I'd use a nullable int as the optional parameter. Using "-1" as a guard feels a bit old fashioned, and -1 vs value is less obvious than null vs value. (The inner routine would still take a non-nullable int).
 
Learn If you want to write code for Python Machine learning, AI Statistics/data analysis Scientific research Web application servers Some microcontrollers JavaScript/Node JS/TypeScript Web sites Web application servers C# Games (Unity) Consumer applications (Windows) Business applications C++ Games (Unreal Engine) Operating systems, device drivers Microcontrollers/embedded systems Consumer applications (Linux) Some more tips: Do not learn C++ (or any other dialect of C) as a...

Similar threads

Replies
55
Views
6K
  • · Replies 3 ·
Replies
3
Views
3K
  • · Replies 3 ·
Replies
3
Views
2K
  • · Replies 7 ·
Replies
7
Views
3K
  • · Replies 3 ·
Replies
3
Views
4K
  • · Replies 8 ·
Replies
8
Views
2K
  • · Replies 3 ·
Replies
3
Views
1K
  • · Replies 3 ·
Replies
3
Views
1K
Replies
2
Views
3K
  • · Replies 5 ·
Replies
5
Views
1K