The easiest way to debug Fortran and Basic is to put in many little print statements, and then to take them out when they are no longer needed. In Java, System.out.println statements are far from little. The quick way to solve the problem is by building a macro-expanding preprocessor. I took advantage of the split method of Java 1.4 and later and the deepToString method of Java 1.5 and later to write something very short. I needed a name for my work, so I looked in Google and did not find PrintMacroJ anywhere, so that is the name that I use.
Having written a preprocessor, I added a few more macros for other things besides printing. I put in a macro to help with regular expressions, a macro to delete underscores in numerical fields as in Ruby, a swap macro, a macro for milliseconds, a few macros to make methods look more like functions, and some macros for overloading operators.
I respectfully invite everybody to add new macros to PrintMacroJ by merely adding new else if paragraphs to the manage method of the Print class. It is the only class in the Print.java file. Please note that the Print.manage method is called from three files: Print.java, AllPri2Java.pri, and Update.pri .
To top
The print macro
Sun’s Java computer language lacks an easy print statement. I mean something like
print eye x(Tripling the blanks is for easier reading. Single blanks will separate just as well.) When I am debugging a one-thread number-crunching console program, that is the kind of statement that I want. Indeed, when I am debugging, I want the statement to print out not only the numerical value but also the formula from which it came. I want the print statement to work not only primitives and objects having toString methods but also arrays, including arrays of arrays, and so on. Maybe other programmers do, too.
Java 5 and later makes it easy to build such a thing oneself. I have written a macro-expander, whose source file is Print.java, having a macro named print to make use of the java.util.Arrays.deepToString static method. That is, the print macro writes a call to that method into the output file.
PrintMacroJ contains other macros besides print. The macro-expander splits each line of the input file on blanks and tabs into tokens having no white-space in them. If the left-most token of the line is recognized as the name of a macro, that macro is used. Otherwise, the line is passed through unchanged.
To top
An example
For a concrete example, I have written a file called CheckSort.pri having some print statements. This file is meant to repeatedly create a float array, fill it with random numbers, print the array, sort the numbers, and print the array again. This is something often done in numerical analysis, to see if the programmer understands correctly what Math.random and java.util.Arrays.sort do.
This file is expanded to a file named CheckSort.java which can then be compiled by javac and executed by java. The reader who looks at CheckSort.java will notice how monstrous and unreadable the print macros have grown.
Here is what the program printed when it was run:
eye, 0, x, [0.29298615, 0.72581846, 0.06552495, 0.20124246] eye, 0, x, [0.06552495, 0.20124246, 0.29298615, 0.72581846] eye, 1, x, [0.17495424, 0.23389111, 0.96421194, 0.59524924] eye, 1, x, [0.17495424, 0.23389111, 0.59524924, 0.96421194] eye, 2, x, [0.6218451, 0.81528014, 0.9022297, 0.20639604] eye, 2, x, [0.20639604, 0.6218451, 0.81528014, 0.9022297] eye, 3, x, [0.06735267, 0.06981697, 0.3623766, 0.65278155] eye, 3, x, [0.06735267, 0.06981697, 0.3623766, 0.65278155] eye, 4, x, [0.4803864, 0.9918647, 0.97115314, 0.78323096] eye, 4, x, [0.4803864, 0.78323096, 0.97115314, 0.9918647] eye, 5, x, [0.40152532, 0.057389356, 0.98135525, 0.7244261] eye, 5, x, [0.057389356, 0.40152532, 0.7244261, 0.98135525] eye, 6, x, [0.9820171, 0.0944072, 0.45101392, 0.6934208] eye, 6, x, [0.0944072, 0.45101392, 0.6934208, 0.9820171]Without the print macro, this would have been much less convenient to program.
All these quoted and unquoted arguments are connected with commas between them. Then curly braces are put on the left and right. Then this formula is used to initialize a new Object[]. Then this Object[] is handed to the deepToString method, as I have said. Then the resulting left-most and right-most square brackets are removed by a regular expression. Then System.out.println is used to actually do the printing. Another look at CheckSort.java may make this clearer.
The CheckSort.pri and CheckSort.java files have exactly the same number of lines, because Print.java always outputs exactly one line for every line it inputs. This is necessary for understanding javac’s diagnostics.
To top
How I wrote and ran CheckSort
To write and run the CheckSort program I opened a Dos box on my Windows computer, made sure that javac was in my path, and typed on the command line
set left=CheckSortThen I wrote the CheckSort.pri file in a simple Windows editor and saved it. Then I clicked back to the Dos box and typed
prito start the pri.bat batch file. That batch file executed javac and java for me, to save a lot of typing. Of course, my batch file is for Dos, and the user may be on some other platform. I think that changing pri.bat to use Unix or Linux may be easy. I have no idea how to change it to use Macintosh.
I have in June 2007 changed the pri.bat file to use the -server modifier on the last line. This is meant to increase run-time speed, but sometimes it makes the program go slower.
To top
Putting javac into the path
Linux and Unix users already know all about paths, so this section is for Microsoft Windows users. (I do not know what to do on a Macintosh platform.) I include a file called findJavac.bat in the download. After the .zip file is unzipped, just please type
findJavacat the Dos prompt, (and push enter,) and all the instances of javac in the same drive as the Dos box will be found. Here is what it found for me:
C:\Program Files\Java\jdk1.6.0\bin\javac.exe C:\Program Files\Java\jdk1.7.0\bin\javac.exeAs I write this, version 6 is stable and version 7 is in beta, so I wish to add version 6’s javac to the path. The trick is to form a new file called, say, path6.bat, by redirection. Just please type
findJavac > path6.batand open path6.bat in an editor. Inside the editor, we see
C:\Program Files\Java\jdk1.6.0\bin\javac.exe C:\Program Files\Java\jdk1.7.0\bin\javac.exeas I have said. Now please delete the second line and the mention of javac.exe, and put quotes around what remains of the first line:
"C:\Program Files\Java\jdk1.6.0\bin\"and then please make it into a command to tell the computer to put it at the beginning of the path:
set path="C:\Program Files\Java\jdk1.6.0\bin\";%path%and save the file and close the editor. Then typing
path6at the Dos prompt will put the address of javac version 6 at the beginning of the path of that Dos box. It will stay there until that Dos box is closed. When the user comes back from lunch and a new Dos box is opened, just please type
path6again, and javac version 6 will be put in the path again.
Also, deepToString is not found on Javas earlier than Java 5.
To top
The pri and pr and prin macros
The print macro causes both the formula and the value of the formula to be printed, for each argument. Printing the formula may be unnecessary. To print only the value of each argument, with commas between, use the pri macro instead of the print macro. Changing the earlier print example to a pri example we get
pri eye x
To print the values without the commas, use instead
pr eye xThis is sometimes handy when the line is to be read by a program instead of a human.
To print the names eye and x but no values or commas, use instead
prin eye xThat is, everything after the prin and its immediately following blanks and/or tabs will be printed at Java run time without changes. In order for this to happen correctly, the prin macro doubles each backslash.
deleteUnderscores long n=4_687_968_574_463_210_974L;is changed to
long n=4687968574463210974L;Strictly speaking, this is a micro, not a macro. That is, it makes things smaller instead of bigger.
doubleBackslashes String yearPattern="\d\d\d\d";to get
String yearPattern="\\d\\d\\d\\d";
milliseconds aand this changes into
long a=System.currentTimeMillis();The reader is respectfully invited to click on Sincos.pri and on Sincos.java to see how the macro is used. It is important to divide (b-a) by 1e3, and not by 1000, so as not to lose the fractional part of the quotient.
swap a b temporaryIt will expand to
{ temporary=a; a=b; b=temporary; }
There must be exactly three arguments to the swap macro. The macro is not able to declare the arguments, so the programmer must arrange for them to be declared before the swap is used.
For example, consider the file Midpoint.pri. It contains a method called midpoint for doing numerical approximations to definite integrals. The midpoint method returns a double. Its parameters are an Integrand called i, a double called a, another double called b, and an int called n. The doubles and ints are familiar. Integrand is an inner interface of the Midpoint class. The Integrand interface contains a double method called f, which has a double parameter called x. The Integrand interface is constructed by the myInterface macro:
myInterface Integrand double f(double x)Here myInterface is the name of the macro, and it means we need to construct an inner interface in the top-level class that we are now in. The name of the inner interface is Integrand. Then there is a kind of signature, giving some information on the method. There is not any semicolon, because the macro will provide that here. The name of the method, f, is part of this signature. The name of the variable, x, is needed by the macro, so it too is part of the signature. Of course the user can change these names, only taking care to agree with herself.
An example of using the Integrand interface is the line
function Integrand multsin return mult*sin(x);It says to implement the Integrand interface, and instantiate, and put the object into a variable named multsin. The body of the f method is return mult*sin(x); with curly braces and other punctuation to be put on by the macro. That semicolon is part of the method body, not part of the macro syntax. Readers wishing to see the result can look at Midpoint.java. Then the result of plugging the multsin object and the doubles and the int into the midpoint method can be printed.
The function call is all on one line. Some method bodies take up too much space for one line, so there are two other macros to work them. They are beginFunction and endFunction. In the Midpoint.pri file they go around the fibonacci’s body. The beginFunction macro has only two arguments: the name of the abstract class to be extended, and the name of the variable to hold the object. The endFunction macro has no arguments.
To top
The stack
The beginFunction and endFunction macros are like parentheses. That is, they must nest correctly, and there must be equal numbers of each. The same goes for the for and next macros farther down in the present file. The same goes for the firstly and lastly macros after that. The macro system uses a stack to see if the nesting and equality rules are obeyed. Any diagnostic saying the word “stack” is talking about wrong nesting or failure of equality.
To top
The yourInterface macro
So far we have been considering only one top-level class, for example, Midpoint. Now it is necessary to think how to get to the Integrand inner interface from a different top-level class. The example is the UseMidpoint.pri file. It includes the line
yourInterface Midpoint.Integrand double f(double x)The difference between yourInterface and myInterface is that yourInterface merely declares the interface and the “signature” of its method, but myInterface also constructs the interface there on the line of the output file. In UseMidpoint.pri we are referring to a different top-level class from our own, so we use yourInterface and give both parts of the name: the top-level class and the inner interface. We do have to give the signature, because there is no short, easy, dependable way for a macro-expander to see inside the other top-level class. The function call now looks a little different from how it looked in the Midpoint.pri file:
function Midpoint.Integrand sinc return Math.sin(x)/x;This is because we must again give both parts of the class name: top-level and inner. The same would apply if we used the beginFunction and endFunction macros. Similarly, the method names are preceded by class name and dot.
myInterface Real2Real double f(double x)This is for functions from the reals to the reals. Then the compose function takes two Real2Reals and composes them and returns the result as a Real2Real. The main method checks to see if this happened correctly.
Imagine that two different programmers were assigned to write different methods. The first programmer was to write a method to approximate the maximum of a function defined on a rectangle by picking points at random and taking maximum. The second programmer was to write a method to approximate the minimum of a function defined on a rectangle by picking points at random and taking minimum. In the real world, they would write different files and e-mail them in. However, to spare the reader’s nerves, I have put all their work in the same file. Also I have put in that same file a main method to call both methods. The file is called Max2.pri.
The reader sees at once that each programmer used a different interface, but the two interfaces have the same signature. (The signature to use was told to the programmers before they started.) That means that it is possible to implement both interfaces at the same time. We do this on each beginFunction and function line by putting the names of both interfaces in the same token with a comma between the names. The result of expansion of the macros is of course in the file Max2.java.
The reason that this works is that the functions were used only as parameters to the methods, but the methods did not return functions. The Java compiler will always catch a blunder of this kind, so the programmer need not worry about it. Just program what is desired, and if it is not permitted then javac will say so.
If there were three files instead of one, then the file with the main method would have to use yourInterface macros, and all the interface names in that file would have to have both top-level class and inner interface with a dot between, as I said above. Similarly, the method names would have to be prefixed with their top-level class names and a dot, as above.
To top
Two other files using functions
Functions are so much fun that I wrote two more numerical analysis files to use them. The first is GetRoot.pri. It contains a method called bisect which does the bisection algorithm for finding the x value where a continuous function crosses the horizontal axis. The inner interface containing the signature is called Continuous, and the user is on her/his honor to use only continuous functions. Actually, the last function in the file is discontinuous, so the algorithm fails.
The second file is HalfStep.pri. It contains a method called halfStep for solving systems of ordinary differential equations with initial conditions by the half-step algorithm. The inner interface containing the signature is called Derivative, and each Derivative function receives a double array of values, and computes from it a double array of derivatives at those values. I leave it to the user or to her students to write the Runge-Kutta algorithm in the same style. The signature need not change. It suffices to write a new method to compete against halfStep.
To top
Using a throws clause
Sometimes a function needs to throw an exception. The file Thrower.pri is an example. It includes the line
myInterface TableWorthy double f( double x ) throws Exceptionwhich shows a signature including a throws clause. The TableWorthy type is needed by the printTable method. In the main method a function called t is built which is passed to printTable along with three numbers telling where to start, where to stop, and what the step is. The t function throws an exception if its argument is out of domain.
The main method defines a function h which is the standard univariate normal without its normalizing denominator. The function p makes a symmetric proposal using the centered uniform distribution. The function r increments some totals and, once in every million times, prints average and variance. If everything goes well, the average will approach zero and the variance will approach unity.
The file is copied from the ideas of Nicholas Metropolis, but he is not responsible for any errors I may have made. I have made no provision for tuning the algorithm or burning it in, but it seems to work anyway.
To top
The Metropolis algorithm: statistics
That previous section worked a probability problem. Actually, in our time the algorithm is used mostly for statistics, and indeed for Bayesian statistics. The LogMetro.pri file is meant to illustrate this. First of all, the letter x is replaced by the letter p, because the random walk is in parameter space, not in sample space. The myInterface Height double f(double[] x) line is replaced by the myInterface LogHeight double f(double[] p) line. This is because we will use logarithms of heights instead of the heights themselves, in order to avoid overflow and underflow. Then the metropolis method is replaced by the logMetro method. In the logMetro method, the calculation of ratio is different: we use the exponential of the difference of the logarithms of the two heights.
The LogMetro.pri file purports to work Darwin’s experiment on crossed and self of Zea mays by a Bayesian method, not by the frequentist method of Fisher. See pages 16ff of http://darwin-online.org.uk/content/frameset?itemID=F1249&viewtype=text&pageseq=414&keywords=zea%20mays for Darwin’s numbers and Galton’s comments. The x array in the main method contains the differences of the two plants in the same pot, following Fisher, not following Galton. The density model I use in the new method logDensity is exp( -abs(x-center)/scale )/(scale*2), which is not Gauss’s normal density. As Galton remarks, quoted by Darwin, the “law of error” seems not to apply here. Instead of making scale a parameter, I make the logarithm of scale a parameter. (This is a common practice.) The prior probability is uniform for the two parameters: center and logarithm of scale. The logHeight function merely adds up the logarithms of the heights for the x’s.
The propose function is much like that in the previous section, but now it works in two dimensions. The report function counts how many times the center was negative, and every million times around the big loop it prints the total number of times, the number of negatives, and their ratio: leftProb. When I ran the program, leftProb appeared to approach a number slightly smaller than .0014 . That is, if the algorithm and model and prior are appropriate to this problem, (and if I understood and programmed all this correctly,) then the probability that self makes bigger plants than crossed is slightly smaller than .0014, and the probability that crossed makes bigger plants than self is slightly larger than .9986 .
Users of the Metropolis algorithm are as always reminded that (1) the algorithm need not converge, (2) it might converge to a wrong value, and (3) even if it does converge to the right value, there is no way of knowing that this has happened.
To top
The ShadedContours program
This program was changed in four ways on 17 August 2007. Users who are downloading PrintMacroJ for the first time may skip to the next paragraph. (1) The way of removing the decoration is now the Esc key only. Other keys will no longer work. This is to make the use of the Alt and PrintScr keys easier. (2) The Esc key does not make a second window, but only removes (or restores) the decoration. (3) Clicking on the decoration to close a window will close only that window. Other windows will stay open. (4) When a window is opened for the first time, it takes up as much space on the monitor screen as the operating system will permit.
It appears that there are two kinds of contour-plotting programs. One kind plots functions, and the other kind plots matrices of numbers. ShadedContours.pri is the first kind. There is a main method at the bottom of its file to make a plot of the contours of Math.tan(x*y). The title of the plot is Tangent of Product. The function which calculates Math.tan(x*y) is named tangent. The x values lie between -5 and 5, and the y values lie between -5 and 5. The number of shadings between contours is 6.
When the program is started by means of the pri.bat file, a “decorated” window will open showing white pixels for very positive function values, nearly black pixels for very negative values, and shades of gray in between. Where white touches nearly black there is a discontinuity. The contours are the boundaries between the shadings, so they are not separately drawn. The window can be dragged and resized, and its contents can be copied to the clipboard by holding down the Alt key and pressing the Print Scr button. Then the clipboard can be pasted down into Microsoft Word, or some other high-quality program, to be printed. (The reader can guess that I use Microsoft Windows on my computer.) WordPad is not high quality for images.
Why is the window decorated? So that it can be dragged and resized and hidden and closed. Unfortunately, the decoration gets in the way of some of the contours. What we want is a way of removing the decoration. The way of removing it is to click the window with the mouse to make sure it has the focus, and then to push the Esc key on the keyboard. Then the decoration will be removed. (Pushing Esc again will decorate the window, and so on.) The Alt and Print Scr keys can be used either with or without the decoration, but without looks better. To end the program, just put the decoration on, if it is not on, and click on the “X” of the decoration to close the window.
Of course, the ShadedContours class will usually be instantiated from another file. An example is UseShadedContours.pri. This file plots two different windows, one for Math.sin(x*y) and one for Math.log(x*y). The latter window has 12 shades instead of only 6. There is a yourInterface declaration, and the inner interface ShadedContours.Altitude gets its full name. The two windows open with the same location and same size, so one must drag and resize to see both at the same time. Then they may be undecorated by the Esc key as before. The “X” close icon on the decoration of a window will close only that window, so two clicks are needed to end the program, one click on each decoration.
At first the contours for Math.sin(x*y) may look like those for Math.tan(x*y); the boundaries are from the same family of hyperbolas. However, the shading is different, and white and nearly black on the sine window can never touch each other. This is because the sine function is continuous. The contours for Math.log(x*y) are new. The second and fourth quadrants are dead black, even blacker than the nearly black mentioned above. This is to signify NaN, because logarithms of negative numbers are not real.
Now to explain how the ShadedContours program works. All the trickery is in the paint method. The double for loop for the variables eye and j looks at each pixel position in the window. The x is calculated from j and the y from eye. Then the altitude function is used on x and y to get z. If k is 1, then the z’s are collected in the alts array and a count kept of the NaN’s. Then the array is sorted, and when k is 2 the rank of z is found by binary search. The rank determines the shade of gray. That is to say, no calculus is used at all, and singularities and discontinuities are easily handled. (However, the z difference between the first and second contours is usually not the same as the z difference between the second and third contours, and so on.) Of course, this algorithm could not be used until computers came to their present speed and size. The problem of plotting contours is not solved; it is overpowered. The reader is respectfully invited to compare the present algorithm against its old-style calculus-using competitors, especially in the case of Math.tan(x*y) .
To top
The unclass.bat file
Using inner classes can make a lot of .class files, and one needs a simple way to get rid of them. A batch file named unclass.bat is provided for Windows users to use at the Dos prompt. It will delete all .class files in the current folder and in all its subfolders. Yes, this deletes Print.class. No, this will not cause trouble, because the pri.bat file always looks to see if Print.class exists, and recompiles it if necessary.
In fact, I do not provide a Print.class file, or any .class file, in the zip. Print.class is compiled the first time pri.bat is used.
To top
The AllPriUnJava.pri file
It is not only .class files which need to be deleted now and again. The .java files written by the pri.bat batch file become too numerous after a while. What we need is a way of deleting only these .java files but not the other .java files. A special program called AllPriUnJava.pri is furnished for this purpose. To use it type and enter these two lines at the Dos prompt:
set left=AllPriUnJava priThen all the .java files in the current folder (except FileNames.java) which have .pri files to them in the current folder will be deleted. Yes, AllPriUnJava will even delete its own newly-built .java file.
The old name of this file was UnJava.pri, but then I saw that the old name was unclear in its meaning, so I changed it.
To top
The AllPri2Java.pri file
Sometimes deleting all those .java files turns out to have been a mistake. To get them all back, a program called AllPri2Java.pri is furnished. To use it type and enter these two lines at the Dos prompt:
set left=AllPri2Java priThen all the .pri files in the current folder (except those whose .java files exist and are younger than their .pri files) will be macro-expanded to .java files. This does not apply to FileNames.pri.
set left=PriUpdateJava priat the Dos prompt, and all the .java files in the current folder that are older than their corresponding .pri files will be replaced by newly expanded .java files. “All” means all except the FileName.java file. Of course, .java files having no .pri files will not be affected.
What is needed is a macro. I have written it and named it let to remind me of Basic. One may write, say,
let x = yy + zzz * wwww ^ vvvvvand it will get changed to
x=add(yy,multiply(zzz,power(wwww,vvvvv)));The reader will notice three things about the let call. Firstly, there are blanks around the variable names and operators. These are required to separate them. Secondly, the let call has no semicolon at the end, any more than the print call does, because they are both macros. Thirdly, the operators change into static method calls, not instance method calls. This is for generality, because sometimes we wish to use operator overload for primitives, and they cannot have instance methods. Also, we may wish to use a final class, and its instance methods may not suit our needs.
I have been assuming that the static methods are in the same class as the line containing the let call. If they are in a different class, say, RatioStatics, then we use
staticsClass RatioStatics let x = yy + zzz * wwww ^ vvvvvand the result is
x=RatioStatics.add(yy,RatioStatics.multiply(zzz,RatioStatics.power(wwww,vvvvv)));As long as the computer does all this typing, I don’t care how tedious it is. The staticsClass call need occur only once in the file, earlier than the let calls to which it belongs. Also, it is legal to use more than one staticsClass call, changing to suit the let calls in different parts of the file.
Three other operators known to the let macro are ** and new and fresh. The ** operator is an exact synonym of the ^ operator, but it imitates Fortran instead of Basic. The new operator has the same meaning as in ordinary Java. The fresh operator means use a constructor. Here is an example of its use:
constructorsClass RatioStatics let x = fresh 5,6where 5,6 is written solid with no blanks. It will change to
x=new RatioStatics(5,6);Here I have assumed that x was already declared. If it was not, we may use
constructorsClass RatioStatics let RatioStatics x = fresh 5,6and get
RatioStatics x=new RatioStatics(5,6);because the let call may take modifiers and class names on the left hand side. The constructorsClass call need occur only once in the file, earlier than the let calls to which it belongs. Also, it is legal to use more than one constructorsClass call, changing to suit the let calls in different parts of the file.
Parentheses are permitted in the let call, and they have their usual meaning. Commas are permitted too, and they are useful for static method calls:
let int eye = RatioStatics.compareTo ( x / y , u - v )Please remember about those blanks for the parentheses and commas.
Let me say a word about subscripts. The let macro does not know anything about subscripts. Nevertheless, they may be used, provided that they are written solid with no blanks. Here is an example, with extra-wide spacing:
let x[i][j] = y[i] + z[j]The tokens containing the subscripts look like variable names to the let macro, so the result is, assuming staticsClass is blank,
x[i][j]=add(y[i],z[j]);just as one would hope.
The let macro can do not only the binary + and - operators but also the singulary + and - operators. The singulary operators are changed to plus and minus, respectively, but the binary operators are changed to add and subtract, respectively.
Also, the let macro can do the ternary operator and relational operators and boolean operators. A contrived example is
let DemoTernary x = ( y < z ) ? y + z : y * zA contrived example file is DemoTernary.pri, and it is changed to DemoTernary.java. The allowed relations are < > <= >= == != and they change respectively to lt gt le ge eq ne. The allowed booleans are && || ^^ and they change respectively to && || ^. I know that there is no such thing as ^^ in Java, but I had already used ^ for a power symbol, as in Basic. I apologize to everybody. I have not furnished a ! operator for negation, and I apologize again.
The let macro is meant for primitives and for immutable objects. It cannot do anything to mutable objects except instantiate them.
To top
RatioStatics
Here is the RatioStatics.java file. A file to use it is DemoRatioStatics.pri, which is expanded to DemoRatioStatics.java.
The demo program is trying to find the square root of 5/6 by Newton’s loop. Rational arithmetic can be slow, but for theoretical math it is great fun.
To top
BigDecimalStatics
Here is the BigDecimalStatics.java file. A file to use it is DemoBigDecimalStatics.pri, which is expanded to DemoBigDecimalStatics.java. The reader will notice the line let BigDecimalStatics.mc = new MathContext(60). If that line were omitted, the default value of 50 digits would be used. Also, the reader will notice that the exponents of x are not marked fresh. This is because they are ints, not BigDecimals. The program is trying to solve the equation x5 + x + 12345 = 0 by Newton’s loop.
To top
DoubleStatics
Here is the DoubleStatics.java file. A file to use it is DemoDoubleStatics.pri, which is expanded to DemoDoubleStatics.java. The program attempts to solve the equation x5 + x + 1.23456789 = 0 by Newton’s loop. The let statements could be written in regular Java almost as easily, but the ^ operator for power would not be available. The reader will notice that the new and fresh key words are not used in this program, because the doubles are primitives.
The DoubleStatics.java file has methods for doubles but not any for ints. This means that ints will be promoted to doubles. Here is an example:
public class Junk
{
public static void main(String[] args)
{
staticsClass DoubleStatics
let double x = 2 / 3
print x
}
}
The printed value of x will be 0.6666666666666666, not zero. This is what Dartmouth Basic did in the old days, and what TrueBasic and JavaScript and Lua do now, but it may not be what the reader expected a Java program to do.
The package is merely a subdirectory. It is not a Java archive (.jar file,) so please do not change the classpath. The package does not yet contain a .class file, but when the user compiles a file referring to the package, say by typing pri at the Dos prompt, javac will write the .class file for the package without saying anything.
A user who wishes to change compPivMat\CompPivMat.pri should keep in mind that everything must be done from the parent directory, not in the package. For example, one might type at the Dos prompt
set left=compPivMat\CompPivMat notepad %left%.prito open an editor. (I am thinking of Microsoft Windows here.) After making changes and saving the file, one can use the priOnly.bat macro. At the Dos prompt one types
priOnlyto macroexpand the .pri file to a .java file and call javac on the result. There will be no attempt to run. This is because there is no main method. If there is a main method, pri cannot run it. If by mistake the user uses the pri at the Dos prompt instead of the priOnly, the files will be correctly changed, but then there will be a frightening diagnostic which may be safely ignored.
I nearly forgot to say what DemoCompPivMat.pri does. It uses least squares to fit a polynomial of degree k-1 to some xData,yData points.
To top
Let and Dac
As the reader has probably guessed, Let is a tiny compiler. It is a top-down recursive syntactic recognizer without look-ahead or back-up. Its source is the Let.dac file. This is converted to the Let.java file by the Dac program, whose source is the Dac.java file. Dac is a tiny compiler-compiler. Dac stands for “dollar, altern, and catena.” The dollar control means “zero or more examples of.” (Back in the fifties this was an actual dollar sign.) The altern control is an alternation, that is, it is a list of things one of which must be true or the pattern fails. The catena is a concatenation of things, all of which must be true in order, or else the pattern fails. If the first thing succeeds but a later thing fails, the whole program quits with a diagnostic, because the program cannot back up. If the first thing fails, the pattern fails, and some other pattern can be tried. Pieces of Java written inside curly braces do not count as parts of the catena pattern. They are only semantic, usually. The Let compiler has a boolean called toggle. The controls and other parts of the program use this toggle to communicate with each other.
To top
Overloading operators for mutables
Using let on primitives is speedy, because the -server optimizer will see what is going on and speed the code up hard. Using let on other peoples’ final immutables, such as BigDecimal and BigInteger, will not slow them down much, because they are already slow. On the other hand, we user programmers write complex-valued arithmetic ourselves, starting with two double variables, one for real and the other for imaginary, and immutable objects will slow these down unconscionably.
The web has few examples of mutable complex-value arithmetic, but there are some, and they are unexpectedly speedy. I will say later in this file how speedy. Here is what a call of the mutate macro may look like:
mutate x := y + z * wThe reader sees the word mutate instead of let and the := operator instead of the = operator. This is to emphasize that x is not a new object. It is the same object as before, but its contents are changed. On the other hand, it is important not to mutate the variables y, z, and w. If constructorsClass is ComplexMutate then the line expands to look like
{ multiply(temporary0ComplexMutate,z,w); add(temporary1ComplexMutate,y,temporary0ComplexMutate); copy(x,temporary1ComplexMutate); }
The “temporary” variables hold the results of the multiply and add. Those temporary variables cannot construct themselves, so we use a macro called makeTemps to construct them. The call
makeTemps 10will build ten of them, from temporary0ComplexMutate through temporary9ComplexMutate. They will end with the name and have the type defined by constructorsClass, and again I have assumed that it is ComplexMutate. If ten is too few, javac will complain, and the programmer may double to twenty, say. If ten is too many, the mistake is harmless.
Each method of a .pri file needs its own makeTemps calls, and they must come before the mutate calls which need them.
To top
ComplexMutate
The file of static methods is
ComplexMutate.java. The reader can see that each static method is careful to mutate only the outward parameter, no other. I point out an oddity: the abs and taxi methods return both real and imaginary parts, but of course their imaginary parts are zero. Those two methods must do this because all the “temporaries” of a mutate call must have the same type.
A demo program is DemoComplexMutate.pri which becomes DemoComplexMutate.java. I have made a point of not using fresh inside the loop, because that would construct and destruct objects inside the loop, exactly what we wish to avoid. I do need many named variables to move the freshes out of the loop.
Now here is something new, not like the “demo” programs. It is SincosComplexMutate.pri and of course its .java file SincosComplexMutate.java. This is a particularly crude form of Euler’s algorithm to solve the sine and cosine ordinary differential equations starting from zero for the sine and one for the cosine and going around the loop a billion times. (American billion.) On my computer this Java program takes 15 seconds to run. For comparison, the corresponding program in Gnu C++, using the built-in _Complex double, and using the command-line modifiers for greatest speed, takes 14 seconds. A famous proprietary freeware C++ takes 15 seconds, the same as our Java program. Other freeware C++es seem to be slower.
The Euler algorithm was chosen to make the ComplexMutate class look speedy. Unfortunately, most useful programs in numerical analysis have subscripts, and Java programs having subscripts are slow. I have timed the matrix multiplication of complex-valued matrices, and a Java program using ComplexMutate is about 2.5 times as slow as the corresponding program in Gnu C++. (Thirty-two seconds for Java and thirteen seconds for Gnu C++, edge size 200, 100 repetitions.)
To top
Mutate and Dac
As the reader has probably guessed, Mutate is a tiny compiler. It is a top-down recursive syntactic recognizer without look-ahead or back-up. Its source is the Mutate.dac file. This is converted to the Mutate.java file by the Dac program, which I have mentioned earlier in this file.
To top
The for and next macros
The print and let macros resemble statements of the Basic language, so for old times’ sake and readability I add macros to imitate Basic’s for and next statements. The for declares its controlled variable to be an int so that it can be used in Java’s subscripts. Optionally, a step may be defined. Both kinds of fors appear in Eratosthenes.pri. It is changed to Eratosthenes.java.
Now we come to the fun part. The macro-expander checks to make sure that the controlled variable of the for is the variable of the matching next. This is a feature of Basic that other computer languages don’t have, and I find it helps in reading a file with big nests of loops.
I emphasize that the starting and stopping values of the controlled variable and the value of the step must each be a single token having no blanks inside. As usual, the values and keywords and = sign must be separated by blanks. Failure to keep to these rules will prevent the macro-expander from seeing that these statements are the Basic for, and javac will say something unkind.
Now the bad news. The macro-expander cannot check that the step value is an integer. If it has a fractional part, the loop will work wrong. The javac compiler will know that a mistake has been made, but it won’t tell.
The macro system uses its stack to check that the for and its next are using the same controlled variable, and that different for loops nested one inside another are using different controlled variables. The latter check is found also in the better class of Basics and in Fortran’s do loops.
To top
The firstly and lastly macros
The “endFunction” macro marks the end of what the “beginFunction” macro began, and the “next” macro marks the end of what the “for” macro began, but some of the keywords of java have effect for many lines and then finally are closed with a right curly brace that looks like all the other right curly braces. This is hard on a human programmer. We humans need marks at the end to match marks at the beginning. I have accordingly invented the “firstly” and “lastly” macros. For example, instead of “public class HyperbolaTable” one may use “firstly public class HyperbolaTable,” and then one ought to write “lastly public class HyperbolaTable” on the line following the last closing curly brace of the body of that class.
This may make more sense if the reader will be kind enough to click on HyperbolaTable.pri. I do something similar to the main method and to a multi-line if statement. The token “firstly” is erased from its line, but the rest of the tokens on that line remain. The token “lastly” and all the other tokens on its line are erased, so the line becomes empty. To see this, the reader is respectfully invited to click on HyperbolaTable.java.
If the tokens following a “firstly” do not match the tokens following its “lastly,” then there will be a diagnostic, and the macro-expansion will stop.
The firstly and lastly use the same macro-time stack that was mentioned above.
To top
Lonely curly braces and lonelyCurly
Let us define a lonely curly brace to mean a curly brace all alone on its line, except for white-space. It is time to include the lonely curly braces in the macro-time stack. The way is to begin the file with the word lonelyCurly. This word tells the macro system to watch for lonely curly braces and stack them and check them for correct nesting, as it does with the other nesting macros. Here is a nonfictional example to click on: CheckBook.pri. It is changed to CheckBook.java.
To top
The go and stop macros
Java programs are too big, especially the little Java programs. Suppose that I have forgotten the exponential of 3, and that I brought the wrong slide rule to work. Do I really need to declare a class and a main method and put on the Math. prefix? No longer. The reader is respectfully invited to click on Junk.pri to see how little I need to type. The resulting Java file after macro-expansion is Junk.java, and I am glad that I did not have to type that on the keyboard. Anyway, go Junk does a static import of java.lang.Math.* and declares the Junk class and the main method, and stop puts on the last two closing curly braces.
To top
The enforce and forbid macros
These are meant to replace the “assert” statement of Java. They are shorter to type, and they do not require a special command-line modifier. That is, they are for the applications programmer, not the system programmer. For examples of enforce one may look at GetAngleBetween.pri. Please notice the line reading
enforce bigX!=0 bigY!=0This is two enforces on the same line. An enforce throws a new Error if its boolean expression is false.
The opposite of enforce is forbid. If the boolean expression in a forbid is true, then the forbid throws a new Error. Both the enforce and the forbid give complete details when they throw. That is, the “second expression” of the “assert” is not necessary (or possible) with enforce and forbid.
To top
The define and unDefine macros
Now it occurs to me (and perhaps to the reader) that the mutate macro may be an example of “too much.” Back in the days of the “m4” substitution macro system people got by with less. I imitate the use of m4 a little bit in a file called SincosComplexMacro.pri. It expands to SincosComplexMacro.java. The reader sees that the former file has three uses of the define macro, and it defines substitution macros called add, subtract, and multiply. Each of these substitution macros takes three arguments called $1, $2 and $3. These are used later in the for loop:
for( int j=0;j<n;j++ )
{
multiply Temp C H
add S S Temp
multiply Temp S H
subtract C C Temp
}
The four lines inside the loop say: Temp is C times H, S is S plus Temp, Temp is S times H, and C is C minus Temp. This arithmetic is “complex.” My notation is different from that of m4, but mine takes less typing. The Temp and C and H of the first multiply are respectively substituted for its $1 and $2 and $3. Similarly for the other lines of the for loop.
The unDefine macro erases the definition made by the define. For example,
unDefine subtractcould be used after the end of the for loop to erase the subtract and prepare for the defineing of a new kind of subtract.
The setq is special. It changes to a Java assignment statement with equal-sign and semicolon. For example,
( setq s ( add s ( multiply c h ) ) )will be changed to
s=add(s,multiply(c,h));
Also the dotimes is special. It becomes a Java for loop. The tokens must be a left paren, the word dotimes, another left paren, a token for the controlled variable of the loop, another token for the number of iterations, and a right paren. You are correct: the parens do not balance. For example,
( dotimes ( j n )changes to
for(int j=0;j<n;j++){
A right round parenthesis all alone on a line is changed to a right curly brace. That is, the end of the dotimes loop is changed to the end of the for loop. This change occurs only if a dotimes loop starting on a previous line is still unrequited by its final right parenthesis. Nested dotimes loops are permitted. However, these joke macros do not make use of the macro-time stack mentioned above.
The other operators, here add and multiply, are assumed to be names of static methods. An example file is SincosSetq.pri. It is changed to SincosSetq.java. The reader is respectfully reminded that the white-space separating the parentheses and other tokens is obligatory here.
The resulting Java program runs at full Java speed, perhaps because the JIT compiler moves the static methods inline. The Java program is more than fifteen times as speedy as my corresponding Lisp program, sincosSetq.txt, using the speediest Lisp system that I know about, Steel Bank Common Lisp. (Yes, I know the suffix ought to be lisp instead of txt, but most browsers don’t know what to do with a lisp file.) However, I am no Lisp programmer, so it may be that my Lisp program is poorly written, or that I have used Steel Bank incorrectly, or that there is a speedier Lisp. I respectfully invite correction from people who know better than I.
To top
Changes
On 1 August 2007 I corrected a big bug in the PrintMacroJ system. Users who downloaded PrintMacroJ only after that date need not worry about the bug. For users who last downloaded it on or before that date, it is necessary to delete some of the user’s old .java files and build them again. Here is how to do it in Microsoft Windows. First please make sure that javac is in the path. Then please select and copy and paste the following script:
set left=Update pri unclassThen please push enter. In the current folder, the old .java files expanded from .pri files will be replaced by expanding those .pri files. (But .java files having no .pri files will be untouched.) Linux and Unix users will know what changes to make to this script. I do not know what Macintosh users ought to do.
at dot
smtw2gh toadmail com
For other links to Harold Kaplan’s programs, click on programming.htm.
For many more links for languages connected with Java, click on http://www.robert-tolksdorf.de/vmlanguages.html.