Monday, June 18, 2012

Upside Down Arrays & A Common Ancestor

Plenty Of Useful Built-In Methods

REALbasic, the language, provides us with some interesting methods to manipulate arrays: Array, Join, Split, Append, IndexOf, Insert, Pop, Redim, Remove, Shuffle, Sort, SortWith, etc... You'll find an exhaustive list in the Language Array category in Real Software's Wiki.
What you may notice is the lack of a method to reverse the order of the elements in the array. But this kind of manipulation shouldn't be hard to code in REALbasic. Let's give it a try!


It's All About Symmetry

If we develop the reverse process further, all we have to do is to swap elements symmetrically. The first element becomes the last one while the last one become the first one, the second element become the penultimate while the penultimate becomes the second one and so on... As in any swapping process, we'll need a temporary variable of the same type than the array elements. Since we'll swap elements, we only need to go through the first half of the array. So we need to determine the middle index of the array.
Let's start by creating a new module, add a new method named 'Reverse' that extends an array of Integers and create some variables:

Sub Reverse(Extends extArray() As Integer)
  
  //-- Reverse the order of an array of Integers
  
  Dim theUpperBound As Integer = extArray.Ubound
  Dim theMiddleIndex As Integer = theUpperBound \ 2
  
End Sub
As we will just go through the first half of the array, we have to calculate the index of the symmetrical element pertaining to the second half before swapping the elements. The calculation is very simple, it's the upper bound value minus the first half element's index. Once we have all the index we need, we can perform the swapping:
  For theIndex As Integer = 0 To theMiddleIndex
    
    // Calculate the symmetrical index
    Dim theSymmetricIndex As Integer = theUpperBound - theIndex
    
    // Perform the swapping
    Dim theTemp As Integer = extArray( theIndex )
    extArray( theIndex ) = extArray( theSymmetricIndex )
    extArray( theSymmetricIndex ) = theTemp
    
  Next

Special Cases

What will happen if the array is empty or contains only one element? Well, in both cases theMiddleIndex will be 0, which means that the For...Next loop's code will execute once. If the array is empty, trying to store the first element value into theTemp variable will raise en OutOfBoundsException. If the array contains one element only it will 'swap' the element with itself which, obviously is useless... Thus we need to handle those cases with a simple If...Then line that simply return from the method when the array is empty ( Ubound() = -1 ) or when it contains a single element ( Ubound() = 0 ).
In the end our method should look like this:
Sub Reverse(Extends extArray() As Integer)
  
  //-- Reverse the order of an array of Integers
  
  Dim theUpperBound As Integer = extArray.Ubound
  
  // Nothing to do if the array is empty or has a single element
  If theUpperBound < 1 Then Return
  
  // Calculate the first half bound
  Dim theMiddleIndex As Integer = theUpperBound \ 2
  
  For theIndex As Integer = 0 To theMiddleIndex
    
    // Calculate the symmetrical index
    Dim theSymmetricIndex As Integer = theUpperBound - theIndex
    
    // Perform the swapping
    Dim theTemp As Integer = extArray( theIndex )
    extArray( theIndex ) = extArray( theSymmetricIndex )
    extArray( theSymmetricIndex ) = theTemp
    
  Next
  
End Sub
Another way of handling special cases is to wrap the entire code after the 'If...Then' line inside a 'If...End If' block with 'theUpperBound > 0' as the condition. It's a matter of taste. I always try to avoid multiple nested 'If...EndIf' block wherever I can avoid it. That way, I found the code easier to read and also more explicit about what's it's doing.

What About String, Double And Other Data Types?

If you want to use the Reverse() method on an array of string for example, the easiest way is to duplicate the Reverse method. To do so, go to the Module tab, click on the Reverse method in the side list and then type Cmd-D for Mac or Ctrl-D for Windows. Change the name of the newly created method from Reverse1 to Reverse, change the type of 'extArray' and 'theTemp' to String and you're done. You can do this for any other data type you need.

Warning: Although it's the easiest way, just keep in mind that Copy & Paste or Duplicate have never been the BEST way to do things in programming. The only thing it's good at is propagating bugs. That doesn't mean you can't use Copy & Paste or Duplicate, it just means that you should always keep this weakness in mind whenever you're using them.

What Is This Common Ancestor Thing About?

Having said that you must have a Reverse method for every data types existing in REALbasic, it's easy to think that you must also have one for every class in your project if you want to reverse a one-dimensional array of this particular class. Of course, if you adapt the method for an array of a specific class, it'll work fine. But given that in the case of classes, the only thing the method is handling are references, there must be something easier to do. All the classes in REAlbasic have a common parent: the Object class. You can't instantiate Object directly with 'New', but it doesn't mean that you can't manipulate them. Actually, each time you're using a class, you're using an Object instance. So, to implement a single Reverse method that will handle any one-dimensional array of any class, let's implement a Reverse method dedicated to arrays of Object. You just need to duplicate one of you existing Reverse method, rename the newly created method's name to Reverse and change the type of 'extArray' and 'theTemp' to 'Object' and that's it.

The Incomplete Autocomplete...

As a last note and to be complete :-), you should be aware that even if the IDE's autocompletion won't display the Reverse method name, calling the reverse method just works fine.

No comments:

Post a Comment