Make Mine MAPPER #18 -------------------- by Rob Haeuser The Subtlety of Subroutines ---------------------------------------------------------------- The screeching was unbearable. A whining, grinding, high- pitched, unbelievably irritating noise seemed to be emanating from all directions. Swiveling my head back and forth, trying to locate the source of my insanity, it became clear that something on the other side of the partition was afoul. Bolting around the corner to catch whatever-it-was in the act, I was instantly agog at the sight of my fellow co-worker actually thinking! The smoke pouring out of his ears was a sure sign that the gears were headed for catastrophic failure. "Good", I thought. "If I just keep quiet he'll self-destruct. At least the damn noise will stop." But nooooo! Mysteries must be resolved. My mouth moved before I could stop it. "What's the deal? Sounds like you need to lube your lobe." "You're not gonna believe this one", he says. "You got a minute?" My favorite question. Did you ever wonder how many people subscribe to the time- transfer theory, whereby if you need a minute, and I have one, it can be transferred to your temporal bucket from mine, and you end up a minute ahead? Does that mean that I'm a minute behind? Maybe that's why my watch is always running slow... . . . . . Hah! Only kidding. I don't wear a watch. So, anyway, he's got two screens going, both displaying what's supposed to be the same report. The report is like a form, formatted in the output area so that the normal MAPPER headers, including the famous "*=" line, are missing. The problem is that one version had headers, and the other one didn't. "The same code produced both of these. So why does one look right, and the other one has a splat-equals?" Hold on, here comes a snappy reply: "Uh, 'scuse me, but did you say "tame toad", or is it that my ears are still ringing? It dunna look de tame to me, do it? It looks like you parked one of 'em under a tree full of birds." Don'tcha just love it when we talk computer? So sophisticated. Can you say computer? His reply? "Well, no, but it is!" Was that a yes or a no? I was really putting on the pressure now. "Yeah, well, there has to be some difference!" "Well, heck if I can see it!" Seems to me like this went on for more than a minute. I think he enjoys playing stump-the-grump. "Wrong, wrong, wrong! It absolutely can't be the same code. Show me." He did. And it was. Sort of. Actually, the same code was used to create the two reports, but accessed in two different ways. Aha! A clue! The code can be executed as a subroutine, which is how it was originally coded, or as a stand-alone run, a later enhancement. The erroneous report - the one with the unwanted headers - was created when executing as a run: the subroutine works just fine. So what's the deal here? Another clue, of course, was the headers themselves. They looked amazingly similar to those of the run containing the code. "Ah, my dear Watson, we have solved the mystery! You see, they are, without a doubt, unequivocably the run's headers. Behold, the run has no break (BRK)!" When the run was executed, it's headers wound up in the output area. Why? Because MAPPER begins executing a run on line three of the run control report. This particular run has five header lines. So what happens when MAPPER sees data? Bang! Into the output area it goes. Without a BRK, the headers stay in the output area, and the subsequent report is forever flawed. Why does it work when called as a subroutine? The subroutine function (RSR) tells MAPPER at which label to begin execution. The report is scanned for the stated label, but any data or functions encountered prior to it are ignored. So, the headers don't wind up in the output area. Intuitively obvious, right? While we're talking about subroutines, I encountered a weird "gotcha" a while back. In last month's article, while discussing the screen control (SC) function, I mentioned a little bit of code called the Rid Handler (RH). It's sole purpose in life is to take a report and overlay it with a function key bar, allowing controlled display of reports by a run to users that may not be familiar with manual MAPPER. RH can be executed as a stand-alone run, linked to from another run, or called as a subroutine by another run. The command handler (CHD) function is used to try and prevent manual intervention in the logical continuation of the run beyond displaying a report. For example, RH is used to display lists of input field selections, one of which can be selected and returned to the calling run by transmitting next to it. You don't want a user to fall off the edge, so to speak, by transmitting accidently from the control line. At least make 'em jump through a few hoops. If the user does transmit from the control line, CHD forces the run to a pre-determined label. At this label, it is determined how RH was executed. If it was executed as a run, or linked by another run, no problem. But, alas! If it was called as a subroutine, the reserved word CRID$, which normally holds the rid number of the calling run, is zeroed out, falsely indicating that the code was not called as a subroutine. So, guess what? If you were to try and do an exit subroutine (ESR), the code would blow up with the message "Subroutine exits exceeds calls". To prevent this, if CRID$ is zero, LINK$ is zero, and the run name in RUN$ is not RH, I issue a warning message to the user indicating that the connection to the calling run has been broken, and will later release to the MAPPER logo (REL) instead of ESR. The bottom line is: to retain as much control as possible when using the command handler, in situations where the code is external to the run, link to the code via LNK. LINK$ retains it's value, even after invoking CHD via transmit, and GTO END will work, no matter what. The next situation isn't so much a problem with subroutines; it can be encountered within a self-contained run. It's just that I first encountered this problem with an external subroutine, and is more likely to happen when using one. Mixing apples and oranges might be great for a fruit salad, as they say, but beware mixing V- numbered variables with named ones. Hi, another old dog here. Yes, I'm still using V1 and V2. I could've had a V8, though. So sue me. But, in the never ending quest for truth, justice, and the MAPPER way, I'll try anything, once. Right. Write a subroutine that initializes named variables, and it will grab the first one available on the stack. In Figure 1, the upper portion is the main run. In it, we initialize a few numbered variables, do a great deal of meaningful work, and then call a subroutine (the lower portion). It initializes a few named variables, adds to the growing heap of done data, and then exits. --------------------------------------------------------------- FIGURE 1 -------- .Date .Runname: DeDummy By: DeDummy *Description: Mixing apples and oranges is messy. * *========= <---------<<<< (The infamous splat-equal line!) @LDV V1H5,V2H5,V8H12,V9H12 . Load up a few variables @. Buncha work going on here, no doubt . @RSR,0,1,31 1 . Execute the external subroutine @LDV V3I7,V4I7 . Try real hard to load a few more variables @. The rest of the run would probably be here . ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .Date .Runname: None (Imasub) By: NDND14U *Description: These are the oranges. * *========= <---------<<<< (Oh, my god! Another one!) @LDV H1,H1 . Real meaningful names, huh? @. More heavy duty work going on, eh? @ESR . Escape back to the point of pending doom ---------------------------------------------------------------- The variable name (Named-Variable-Number-1, of course) will occupy, by default, the next available slot in the stack. Because the main run initialized V1 and V2, then skipped up to V8, the third and fourth slots, otherwise known as V3 and V4, are available and will get used. That doesn't matter to the subroutine. It functions without any problems, returning control to the run when it's done. At this point, the run tries to execute the "@LDV V3I7,V4I7" code, and will fail, giving the error message: "That variable may only be referenced by name". I wish I knew why. After all, it does have a number associated with the name hanging around somewhere, doesn't it? The solution, in this case, is the USE function, which allows us to pre-select the variable number to be eaten up by a named one. So somewhere prior to initializing the named variables, we would have the statement: "@USE NVN1=V101,NVN2=V102 .". Now V101 and V102 may only be referenced by name, and V3 can be initialized without problems. By the way, USE doesn't like a "#" in the name, at least in level 35, and I'll bet there are a few more characters in the set that are off-limits, too. But back to the original header fiasco. Unfortunately, even though the problem was solved simply by adding a lousy BRK statement, the strain of just such a simplistic answer was way too much for gears overheated by the quest for infinite knowledge. He fried on the spot. A complete meltdown! You should have seen the mess on the floor...