Introduction
This article presents an introduction to coding shortcuts in Visual FoxPro - when to use them, and when not to.
Notes on coding in general
This article is about coding shortcuts; however, I should first emphasize that making the code as small as possible is usually not the number one priority when writing computer code. I would say the priorities are - usually in this order:
For instance, you should never omit a reasonable amount of notes in your code, just to make your code shorter, and you should never use variable names that are so short that the reader can't recognize what sort of value the variable is supposed to hold.
On the other hand, less lines to maintain may very well make the entire program more maintainable.
I haven't tried it, but I imagine there could be quite heated discussions whether a command like
a = b = c
should be avoided, in favor of a longer version. You can get accustomed to reading it and interpreting it correctly, and perhaps any good VFP programmer should be able to find out rather quickly what VFP will do if it sees this command.
Be this as it may, in the remainder of this article I will present some interesting ways to make long code short. In some cases, this is done at the cost of readability; the programmer should consider carefully whether it is worthwhile to use a certain shortcut.
Logical expressions
A great amount of coding "mistakes" (in the sense of using too much code) is done in the use of logical expressions. For some people it may be easy to understand code like
if a = b * Do something here endif
but it might cause some difficulty to understand that in most places where you can use the values .T. and .F., any logical expression can be used. This includes saving a logical value to a variable or a field.
Let's see some examples involving logical expressions.
if MyField = .T.
MyField already is a logical expression! Comparing it to .T. is completely superfluous - this will result in exactly the same value as MyField. So, this expression can be simplified to
if MyField
A comparison like "if MyField = .F.", on the other hand, can be changed to "if Not MyField".
Another example:
if MyField MyVar = .T. else MyVar = .F. endif
This can be reduced to a single line:
MyVar = MyField
Remember the general rule: you can assign any logical expression to a logical variable.
And yet another example:
if b = c a = .T. else a = .F. endif
Once again, you can assign the logical expression (in this case, the result of the comparison) directly to the variable:
You may want to use parentheses:
a = (b = c)
but this is not required, since the comparison on the right is done before the assignment, anyway. That is, the parentheses are not required according to the compiler - for the human, the code with parentheses may be more readable, since it is now quite clear what is done first by the compiler.
IF, and shortcut Boolean evaluation
An expression like
if a if b (Do Something) endif endif
can be simplified, combining the two Ifs (this only works here because of the lack of ELSE clauses):
if a and b (Do Something) endif
In this case, the two statements are exactly equivalent, even if you replace "a" and "b" with more complicated expressions.
To fully understand the fact that the statements are equivalent, you should have a basic understanding of shortcut Boolean evaluation. Lets take a statement like
if a and b
It may help to review the truth table of the AND operator: if both parts are true, the result is true. If any of the two parts (or both) are false, the result is false.
That means that if the first part, "a", is false, the result will always be false, whether the second part is true or false. Therefore, there is no need to look at (evaluate) the second part. And this is exactly what the compiler does: ignore the second part.
This may not be very relevant with simple variables, but it can be very helpful with more complicated expressions.
For example, in the following code:
if type("x") = "L" and x
the first part of the condition is meant to avoid error messages. If the first part evaluates to false, the second part will not be evaluated, and there will be no error message ("variable not found" if the variable doesn't exist, or "type mismatch" if the variable happens to have the wrong type).
By the way, with OR, shortcut Boolean evaluation works the other way: if the first part is true, the second part will be ignored by the compiler.
Ignoring return values
VFP functions have a return value. If you don't need the return value, you can just discard it. Some examples follow:
InsMode(.T.)
will turn insert on. The return value (the previous value of insmode) is ignored in this case.
iif(llCondition, Function1(), Function2())
will execute Function1 if the condition is true, Function2 otherwise. Once again, the return value is ignored.
= Function1() and Function2() and Function3()
will execute the three specified functions, one after the other. If any of the functions returns false, the following functions will not be evaluated.
By the way, a more extensible way to obtain the same result, and one that simplifies debugging, is a DO CASE with no statements, only conditions:
do case case not Function1() case not Function2() case not Function3() endcase
Why will this work? Because, if one of the CASEs evaluates to false, VFP will try the next one.
XOR
I have seen the following workaround for the apparent lack of a XOR (exclusive OR) operator in Visual FoxPro:
if (a and not b) or (b and not a)
Of course, this will work, since XOR can be defined this way. However, there is a much simpler way:
if a # b
"#" is the inequality, which you may replace with "<>" or with "!=". You may prefer to make the code more readable, as follows:
#DEFINE XOR # if a XOR b
For more complex expressions, remember that the thusly #defined operator is not a "real" boolean operator. This affects the order-of-precedence. In other words, in an expression like
#DEFINE XOR # if (field1 = 0) XOR (field2 = 0)
the parentheses may not be omitted, since otherwise the equality on the right will be evaluated after the XOR (equality and inequality have the same order-of-precedence).
Know the relevant commands
It helps to know the VFP commands, and use the most appropriate for each case. This may help you avoid long workarounds. This general statement might be expanded to hundreds of examples; let me just give two.
go top do while not eof() ? AnyField skip enddo
Knowing the existence of the SCAN loop - which is specifically designed to skip through a number of records - simplifies this loop quite a bit:
scan ? AnyField endscan
GO TOP, the condition, and the SKIP are all taken care of by the SCAN loop.
The second example shows that in some cases, knowing the commands is not enough - it may require a stroke of creative genius to apply existing functions in innovative ways. For most of us, this means participating in a newsgroup like the Universal Thread, to benefit from other people's genius. (I don't know who first discovered this particular shortcut.)
The problem is to include, in a SELECT - SQL statement, a clause to sort on a certain field. The sort order, however, is not alphabetic, but (sort of) arbitrary, since the field contains specially coded information. For instance, "Q" should come first, next "W", then "E", then "R".
Two obvious solutions are nested iif's, as follows:
SELECT ...,; iif(Code = "Q", 1, iif(Code = "W", 2, iif(Code = "E", 3, 4))) as OrderField; ...
and invoking a UDF which includes a DO CASE and returns the numbers in the desired sort order.
The shortcut - which in my opinion required a stroke of genius to be found - is as follows:
SELECT ..., at(Code, "QWER") as OrderField; ...
AT() simply does a search of the first string within the second, and returns the position, if found.
Summary
You can use a lot of different tricks to make your programs shorter. In some cases, this may make your program code easier to maintain. In others, it may make your code unreadable.