Generate all valid combinations of n pair of parentheses

Posted on

Problem

Implement an algorithm to print all valid (e.g. properly opened and
closed) combinations of n pair of parentheses.

Following is my implementation and I think it works. Is the algorithm efficient? Do you see any coding issues?

def compute_all_parens (n, left, right, s):
    if right == n:
        print (s)
        return
    if left < n:
        compute_all_parens(n, left+1, right, s + "(")
    if right < left:
        compute_all_parens(n, left, right+1, s + ")")



# call syntax: compute_all_parens(5, 0, 0, "")

Solution

API

The compute_all_parens function requires 4 parameters.
From a user’s perspective,
it would be more natural to require only one,
to avoid confusion about what values to pass for the others.
You could use an inner function to encapsulate your implementation details:

def print_all_parens(n):
    def print_parens(left, right, s):
        if right == n:
            print(s)
            return
        if left < n:
            print_parens(left + 1, right, s + "(")
        if right < left:
            print_parens(left, right + 1, s + ")")

    print_parens(0, 0, "")

I also corrected some formatting issues, following PEP8.

Use generators

Instead of printing, it would be nicer to return a generator:

def compute_all_parens(n):
    def compute_parens(left, right, s):
        if right == n:
            yield s
            return
        if left < n:
            yield from compute_parens(left + 1, right, s + "(")
        if right < left:
            yield from compute_parens(left, right + 1, s + ")")

    yield from compute_parens(0, 0, "")

This will give callers the freedom to do whatever they want with the combinations.

It’s fine, but there are some small issues with it.

  1. You call your function compute_all_parens, yet it does not return anything. It prints all parentheses, therefore the name print_all_parens would be more appropriate.
  2. The names left and right are only somewhat descriptive. The name s is completely nondescript. Naming is hard, but as long as you provide that interface to the user, you either need better names or a documentation string.
  3. It provides an unintuitive user interface that’s prone to errors. You could add another function that takes care of that:

    def print_parens(number_of_parens):
        """Prints all valid variants with number_of_parens parentheses.
    
        Examples:
    
            >>> print_parens(1)
            ()
    
            >>> print_parens(2)
            (())
            ()()
    
            >>> print_parens(3)
            ((()))
            (()())
            (())()
            ()(())
            ()()()
        """
        print_all_parens(number_of_parens, 0, 0, "")
    

    You can hide your original function as a closure in there, but I’m not sure whether that counts as best practice.

Leave a Reply

Your email address will not be published. Required fields are marked *