Table Driven Programming
Table driven programming is a technique that can make some programs more readable and maintainable, but is often overlooked by script developers.
Let me give you an example. Here's a fragment of a VBScript program that I ran into yesterday. It's perfectly correct, but it could be a lot shorter.
Select Case wmiAce.AceType
Case 0
OutFile.WriteLine "<ACE type=""Access Allowed"">"
Case 1
OutFile.WriteLine "<ACE type=""Access Denied"">"
Case 2
OutFile.WriteLine "<ACE type=""Audit"">"
End Select
If 1048576 And wmiAce.AccessMask Then
OutFile.WriteLine " <Perm>Synchronize</Perm>"
End If
If 524288 And wmiAce.AccessMask Then
OutFile.WriteLine " <Perm>Write Owner</Perm>"
End If
' [... thirty more lines just like that ...]
If 2 And wmiACE.AccessMask Then
OutFile.WriteLine " <Perm>File Write Data</Perm>"
End If
If 1 And wmiACE.AccessMask Then
OutFile.WriteLine " <Perm>File Read Data</Perm>"
End If
OutFile.WriteLine "</ACE>"
There's nothing broken about this program, and it's perfectly straightforward. But it is so redundant! So repetitive! It says the same thing over and over. It has the same pattern again and again. It just goes on and on with only minor changes on each line.
Ahem. All that redundancy is just plain tiring to the eye, and the potential for typos and other mistakes goes up as the program gets longer. If I were writing this program, I'd write it like this:
AceType = Array("Access Allowed", "Access Denied", "Audit")
OutFile.WriteLine "<ACE type=""" & AceType(wmiAce.AceType & """>"
Access = Array("File Read Data", "File Write Data", [...] , "Synchronize")
For Flag = LBound(Access) To UBound(Access)
If 2^Flag And wmiAce.AccessMask Then
OutFile.WriteLine " <Perm>" & Access(Flag) & "</Perm>"
End If
Next
OutFile.WriteLine "</ACE>">
Which reduces a 51 line program to nine much more readable lines.
Table driven programming is extremely powerful in JScript, because JScript supports sparse associative arrays and first class functions. It's quite easy to define function tables in JScript. For example:
function handleRead() { ... }
function handleWrite() { ... }
// ... etc…
var handlers = new Array();
handlers["Read"] = handleRead;
handlers["Write"] = handleWrite;
// ... etc ...
// later:
var handler = handlers[userInput];
if (handler != null)
handler();
Which is a lot more compact than the equivalent switch statement.
Next time you build a really big conditional statement, ask yourself whether you could summarize all of the choices into a table. It can make a program considerably more readable.
Comments
- Anonymous
February 24, 2004
Speaking of arrays....
is the new Microsoft Scripting Engine (on Longhorn?) going to have better a better array implementation? The so-called dynamic array ReDim is just a joke. ok, I'm a little biased by the lovlieness of Perl's PUSH and POP (not to mention slicing), but it works.
I think that people would make much better use of arrays if they were easier to use! - Anonymous
February 24, 2004
The comment has been removed - Anonymous
February 24, 2004
First off, there will be new scripting technology in Longhorn, but I have absolutely nothing to do with that. I don't know what the features will be, I don't know availability or schedules or anything. The old script team is not working on that project; we are all working on other language and programmng tools.
Second, I agree that arrays in VBScript are pretty 20th century. But as I have mentioned many times in my blog, JScript has sparse, associative object-based arrays rather similar to perl. You can push, pop, slice, sort, etc, a JScript array.
Third, in VBScript you can use the dictionary object as an associative array. - Anonymous
February 24, 2004
Note that push, pop, shift, unshift, and all you other friends (and mine) from Perl are only available in JScript as of version 5.6. It's a free download, so it's not a real issue, but it might have been confusing if correct code (must be correct 'cos Eric said so ;) did not work for you. - Anonymous
February 25, 2004
The BeyondJS library makes extensive use of push, pop, shift, unshift, etc. Consequently, it provides an implementation of these functions for JavaScript versions that don't support them. For example:
if ( typeof(Array.prototype.pop) != "function" )
Array.prototype.pop = function() {
if ( this.length ) {
var item = this[this.length-1];
--this.length;
return item;
}
}
You can find BeyondJS at:
http://w3future.com/html/beyondJS/ - Anonymous
March 01, 2004
This could make an interesting way to do state machines in script, especially using GetRef:
Dim a(2)
Set a(0) = GetRef("a0")
Set a(1) = GetRef("a1")
Set a(2) = GetRef("a2")
Sub a0
'...
End Sub
Sub a1
'...
End Sub
Sub a2
'...
End Sub - Anonymous
March 01, 2004
State machines are an excellent example of a good use of table-driven programs. Even simple state machines.
On page 127 of Writing Solid Code, Steve Maguire gives an example that I quite like. He's got a method that does state cycles, either 0 1 0 1 0 1 or 2 3 4 2 3 4 2 3 4. Which would you rather read? Which one makes more sense?
return ((s<=1)?(s?0:1):(s==4)?2(s+1));
or
next[] = {1, 0, 3, 4, 2};
return next[s]; - Anonymous
March 03, 2007
Why call it table driven programming? Why not array-driven programming? When I think of table driven programming, I think of the real thing: eDeveloper. http://www.magicsoftware.com/edeveloper