Bushy Trees

A favorite peeve of mine is code with “bushy trees.” I first saw this phrase in Kernighan and Plauger’s Elements of Programming Style.

Recently, I saw some code that checked if a RefEdit control referred to a single cell that contained a non negative integer. I cleaned up the formatting some since the original indentation style might be best described as “random tabs.” But, code formatting is not the subject of this post.

The IFs are fairly easy to understand since they essentially follow how most people would think to validate the string parameter. The tricky part comes in ensuring that one has the correct Else clause at the correct indentation level. In this case, that too might not be too difficult since the Else clauses are fairly trivial, each consisting of a single boolean assignment. But, imagine how much more difficult the task would be if there were further If clauses or loop structures in some of the Else clauses!

Option Explicit
Public Function getNonNegInt(aRefEditText As String, _
        ByRef Rslt As Integer) As Boolean
    Dim aRng As Range
    Const MaxInt As Integer = 32767
    getNonNegInt = True
    On Error Resume Next
    Set aRng = Range(aRefEditText)
    On Error GoTo 0
    If Not aRng Is Nothing Then
        If aRng.Cells.Count = 1 Then
            If IsNumeric(aRng.Value) Then
                If CDbl(aRng.Value) >= 0 Then
                    If CDbl(aRng.Value) = CLng(aRng.Value) Then
                        If CLng(aRng.Value) < = MaxInt Then
                            Rslt = CInt(aRng.Value)
                        Else
                            getNonNegInt = False
                            End If
                    Else
                        getNonNegInt = False
                        End If
                Else
                    getNonNegInt = False
                    End If
            Else
                getNonNegInt = False
                End If
        Else
            getNonNegInt = False
            End If
    Else
        getNonNegInt = False
        End If
    End Function

Code Sample 1

As a first pass, one could remove all the boolean assignments by first setting the function value to false and not to true as in Code Sample 1. Then only if we have an acceptable value do we return a True value.

Option Explicit
Public Function getNonNegInt(aRefEditText As String, _
        ByRef Rslt As Integer) As Boolean
    Dim aRng As Range
    Const MaxInt As Integer = 32767
    On Error Resume Next
    Set aRng = Range(aRefEditText)
    On Error GoTo 0
    getNonNegInt = False
    If Not aRng Is Nothing Then
        If aRng.Cells.Count = 1 Then
            If IsNumeric(aRng.Value) Then
                If CDbl(aRng.Value) >= 0 Then
                    If CDbl(aRng.Value) = CLng(aRng.Value) Then
                        If CLng(aRng.Value) < = MaxInt Then
                            Rslt = CInt(aRng.Value)
                            getNonNegInt = True
                           End If
                        End If
                    End If
                End If
            End If
        End If
    End Function

Code Sample 2

However, this still doesn't help with all the nested If and End If clauses.

So, how does one clean up this deeply nested code? How about if we reverse the tests? Instead of testing if the range is not nothing, test if it is nothing. Instead of testing if the range contains 1 cell, test if it contains more than 1 cell. And, so on. The result as shown in Code Sample 3 is a single If statement (with multiple ElseIf clauses) that is 'flat' -- with no confusing nesting!

Option Explicit
Public Function getNonNegInt(aRefEditText As String, _
        ByRef Rslt As Integer) As Boolean
    Dim aRng As Range
    Const MaxInt As Integer = 32767
    getNonNegInt = False
    On Error Resume Next
    Set aRng = Range(aRefEditText)
    On Error GoTo 0
    If aRng Is Nothing Then
    ElseIf aRng.Cells.Count > 1 Then
    ElseIf Not IsNumeric(aRng.Value) Then
    ElseIf CDbl(aRng.Value) < 0 Then
    ElseIf CDbl(aRng.Value) <> CLng(aRng.Value) Then
    ElseIf CLng(aRng.Value) > MaxInt Then
    Else
        Rslt = CInt(aRng.Value)
        getNonNegInt = True
        End If
    End Function

Code Sample 3

The code above uses a very powerful concept -- that of a 'null clause.'

In most cases, when we have a If...Then, we have some statement in the True branch of the If statement. It might be a series of assignments or it might be another If or a loop of some sort but there is something in the True branch. For example, in Code Sample 1 and Code Sample 2 above, there are two assignments.

                        If CLng(aRng.Value) < = MaxInt Then
                            Rslt = CInt(aRng.Value)
                            getNonNegInt = True
                           End If

However, in Code Sample 3, each Then is followed not by a statement but by the ElseIf clause. This results in a null statement (or empty block) in the True branch. This is perfectly legal in every programming language I've used and in this case it serves a very powerful role in simplifying the code.

For the sake of completeness, we will look at one more way of coding the above. In this particular scenario since there is no further processing after the string is validated, one could use use a series of Ifs that simply go to an exit point for bad data. But, this would not work for scenarios in which we wanted to do additional processing after the string of Ifs.

Option Explicit
Public Function getNonNegInt(aRefEditText As String, _
        ByRef Rslt As Integer) As Boolean
    Dim aRng As Range
    Const MaxInt As Integer = 32767
    On Error Resume Next
    Set aRng = Range(aRefEditText)
    On Error GoTo 0
    If aRng Is Nothing Then GoTo ErrXIT
    If aRng.Cells.Count > 1 Then GoTo ErrXIT
    If Not IsNumeric(aRng.Value) Then GoTo ErrXIT
    If CDbl(aRng.Value) < 0 Then GoTo ErrXIT
    If CDbl(aRng.Value) <> CLng(aRng.Value) Then GoTo ErrXIT
    If CLng(aRng.Value) > MaxInt Then GoTo ErrXIT
    
    Rslt = CInt(aRng.Value)
    getNonNegInt = True
    Exit Function
ErrXIT:
    getNonNegInt = False
    End Function

Code Sample 4

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>