Killing Comments
Vector 18:3, page 38
Transcribed from the printed issue; not yet reviewed. Read it in the PDF of the issue, page 38
Utility Corner: Killing Comments¶
(The purpose of this column is to make you more productive by introducing you to utility functions. Think of utility functions as APL functions that have names instead of symbols. By expanding your function vocabulary, you’ll be able to write APL code that’s more concise, more efficient, and more readable.)
In last issue’s LIMBERING UP column, we asked you to design and write a function named UNCOMMENT that eliminates the comments and all-blank lines from a specified function. Normally, we encourage you to include more comments, not to strip them out. Why the change of heart?
Strangely, having an UNCOMMENT utility in your bag of tricks may encourage you to include more comments. Some APL programmers rationalise their lack of comments by claiming comments consume valuable workspace. Less workspace increases the chances of WS FULL errors and may even cause the functions to run more slowly. By omitting comments, they say, the functions run "better". Fine, we say, omit the comments from the version of the functions that you run. But don’t omit them from the version you maintain.
Construct a "development workspace" that contains highly commented functions. Then pass each of the functions through the UNCOMMENT utility function to strip out the comments. Save the uncommented functions in a "production workspace." You’ll have two workspaces: the development workspace is big and readable and easy to maintain. The production workspace is small and streamlined, but unreadable.
The general approach of the UNCOMMENT utility is to convert the specified function to a character matrix by providing its name as the right argument of the system function ⎕CR (Canonical Representation). Here’s an illustration:
∇ R←AVG V
[1] ⍝ Returns the average of the numbers
[2] ⍝ in vector V.
[3] R←(+/V)÷⍴V ⍝ Doesn't work on scalars
∇
⎕CR 'AVG'
R←AVG V
⍝ Returns the average of the numbers
⍝ in vector V.
R←(+/V)÷⍴V ⍝ Doesn't work on scalars
⍴⎕CR 'AVG'
4 36
Next, use the Boolean techniques discussed in the Primitive Pointers sections of this News to identify and blank out comments. Finally, supply the resulting character matrix as the right argument of the system function ⎕FX (Fix). ⎕FX defines (fixes?) the new function in the workspace, using the rows of the character matrix as the lines of the function. The explicit result of ⎕FX is the character vector name of the function defined. Like so:
What should be the syntax of the UNCOMMENT function? We left the design up to you. Of the solutions sent us, two designs were proposed.
In the first, UNCOMMENT is a monadic function whose argument is the name of the function to be "uncommented." The UNCOMMENT function uses ⎕CR to convert the name to a character matrix representation of the function, applies the Boolean techniques to replace the comments by blanks, applies ⎕FX on the resulting matrix, and returns as its explicit result the result of ⎕FX, that is, the name of the redefined function.
A disadvantage of this design surfaces when you try to uncomment a function whose name happens to match the name of one of the local variables in UNCOMMENT. For example, suppose the header of UNCOMMENT looks like this:
If you try to uncomment a function named R, NAME, A, B, or C, the function won’t work. In particular, when UNCOMMENT does ⎕CR NAME the function having that name is "shadowed" by the local variable of the same name. So, ⎕CR tries to return the canonical representation of a variable rather than a function, and gets all fouled up. (It returns an empty character matrix.)
In the second design, this problem is eliminated, at the expense of convenience. UNCOMMENT is a monadic function whose argument is the canonical representation of a function to be uncommented, rather than its name. The explicit result of UNCOMMENT is the amended character matrix. UNCOMMENT does not perform ⎕CR or ⎕FX. Thos operations are left to you. For example,
Design 1:
Design 2:
Gary Logan submitted a solution that incorporates both designs. If the right argument is a character vector (or scalar), UNCOMMENT takes it as a function name, and applies ⎕CR to it. If the right argument is a character matrix, UNCOMMENT takes it as the function’s canonical representation, and applies the Boolean techniques directly. To show us the effectiveness of his UNCOMMENT function, Gary printed the result of UNCOMMENT ⎕CR 'UNCOMMENT' and sent it to us, with the explanation, "It is clear that UNCOMMENT works very well on itself." True, though we couldn’t help but wonder whether this UNCOMMENT function ever contained any comments.
The following UNCOMMENT function is a composite of the functions we received. It contains plenty of comments (why not?).
∇ R←UNCOMMENT A;N
[1] ⍝ Remove the comments from the func-
[2] ⍝ tion name A (if A is a scalar or
[3] ⍝ vector) or from the ⎕CR canonical
[4] ⍝ representation A (if A a matrix).
[5] ⍝ The result is the name of the newly
[6] ⍝ defined function (via ⎕FX) if A is
[7] ⍝ a name, or is the stripped charac-
[8] ⍝ ter matrix, which can be passed to
[9] ⍝ ⎕FX, if A is a matrix. The number
[10] ⍝ of local names is kept to a minimum
[11] ⍝ to reduce the possibility of a
[12] ⍝ named function conflicting with a
[13] ⍝ local name.
[14] ⍝
[15] ⍝ Convert name to ⎕CR if needed:
[16] ⍎(N←1≥⍴⍴A)/'A←⎕CR A'
[17] ⍝ Flag leading (∧\) noncomments or
[18] ⍝ text in quotes:
[19] R←,∧\(A≠'⍝')∨≠\A=''''
[20] ⍝ Squeeze/expand out comments:
[21] R←(⍴A)⍴R\R/,A
[22] ⍝ Eliminate all blank rows:
[23] R←(R∨.≠' ')⌿R
[24] ⍝ Convert the ⎕CR back to a function
[25] ⍝ if a name given
[26] ⍎N/'R←⎕FX R'
[27] →N/0
[28] ⍝ Otherwise, eliminate trailing all-
[29] ⍝ blank columns before returning the
[30] ⍝ matrix:
[31] R←(⌽∨\⌽' '∨.≠R)/R
∇
The last expression in UNCOMMENT is executed only if the argument and result of UNCOMMENT are character matrices. Because the logic in UNCOMMENT does not squeeze out comments, but rather replaces them by blanks, it’s possible for the resulting character matrix to contain a number of trailing all-blank columns, where comments previously existed. As a courtesy to the user of UNCOMMENT, these trailing blank columns are eliminated.
Finally, let’s try UNCOMMENT on itself:
UNCOMMENT ⎕CR'UNCOMMENT'
R←UNCOMMENT A;N
⍎(N←1≥⍴⍴A)/'A←⎕CR A'
R←,∧\(A≠'⍝')∨≠\A=''''
R←(⍴A)⍴R\R/,A
R←(R∨.≠' ')⌿R
⍎N/'R←⎕FX R'
→N/0
R←(⌽∨\⌽' '∨.≠R)/R
What’s the size difference between the commented and uncommented versions of UNCOMMENT? On APL*PLUS PC, the commented version occupies 1776 bytes of actual workspace; the uncommented version takes 384 bytes, or 22% of the original.
What about readability? The commented version is five times the size of the uncommented version. Is it five times more readable? Ten times? Fifty times?
Comments are priceless. Don’t omit them.