Guru Classic: A Bevy Of BIFs — Updates
February 5, 2020 Jon Paris
In my previous tip I returned to the “Bevy” series to tell you about the latest addition to the family: %ScanRpl. Shortly after completing that tip I realized that there have been a number of more recent BIFs such as %SubArr and some enhancements to existing BIFs like %Trimx that seem to have escaped people’s notice. In this tip I attempt to fill those omissions.
%Trim: Specify Characters to Trim
Let’s start with a BIF that hopefully you are all familiar with: %Trim. Before going any further I should point out that when I say %Trim, I am referring to the entire Trim family, i.e., %Trim, %TrimL, and %TrimR.
What you might not realize though is that %Trim now offers an optional second parameter that allows you to specify exactly which character(s) you want to trim off. This can be really useful when processing data from CSV files or Web services where you encounter numeric values with leading asterisks and/or a currency symbol. For example $250.95 or ***125. The sample code below shows how this new option would allow you to “clean up” such values.
Running it will result in the field target receiving the value 123.45 and having a length of six, at this point it is able to be processed by %Dec() for subsequent use in numeric calculations.
dcl-s charAmt2 char(20) Inz('***$123.45'); dcl-s target varchar(20); target = %Trim( charAmt2 : ' *$' );
Note that in order to have blanks removed (trailing blanks in the example) I had to include a space in the string of characters to be removed. This would not have been needed if the intent was simply to use the resulting value with %Dec() since that BIF will automatically ignore leading and trailing spaces.
%Addr: *Data Option
This is one that I’m sure a lot of people missed, judging from the number of examples I see posted in online forums that do not make use of it.
Introduced back in V5R4, the *Data option of the %Addr BIF returns the address of the data portion of a varying-length field, automatically bypassing the two (or four) byte count header portion. I make a lot of use of this capability in programs that build a string iteratively. For example, records to be written to CSV files, JSON, HTML and XML, etc. These sorts of strings are much more efficiently built in varying-length fields than in fixed length, but until this option was introduced it was necessary to manually add two (or four) to the address before passing it to an API. As a result you may encounter this kind of code.
pParmData = %Addr(ParmData) + 2;
Using this new option allows you to code it this way:
pParmData = %Addr(ParmData: *Data);
Not only is this code simpler and more obvious to anyone who has to maintain the code in the future, but it is also much safer.
Why so? Suppose that ParmData was originally 50,000 bytes long, but we subsequently need to increase the size to 100,000 bytes. At this point we have to hope that the programmer tasked with making this change is aware that a varying length field of 100,000 bytes requires a 4-byte header, not a 2-byte. As a result, simply changing the field length is not enough. They must also remember to change the “+ 2” to “+ 4” or the address created will be incorrect. Had the original programmer used the *Data option then this would never have become a problem since the compiler would have automatically calculated the correct address.
Ever since I started coding in PHP, where arrays are a way of life, I realized that RPGers don’t use arrays as much as they should. But even among those who do make use of them, the %SubArr BIF seems to have slipped by unnoticed. Until the advent of the V7.4 release, RPG lacked the ability to use dynamic arrays in the sense that languages like PHP do. %SubArr however does allow us to surmount some of the problems that fixed length arrays can present.
%SubArr allows you to specify that an operation, such as a SORTA, is to take place on only a portion of the array. So, for example, if while loading the array myArray we keep track of the number of active elements in the field count we can subsequently use that value with %SubArr like so:
SORTA %SubArr( myArray : 1 : count );
This allows us to sort only the active portion of the array. Not only is this faster, but it also avoids the need to have previously loaded all of the array elements with high (or low) values to “keep them out of the way” after the sort.
%SubArr() can also be used to assign one portion of an array to another. Similar in some ways to the way in which the old MOVEA operation could be used. In the first example below, elements 5 to 9 of arrayZone are copied to elements 1 to 5 of arrayPack. In the second example the elements 1, 2 and 3 of arrayZone are copied into elements 2, 3 and 4 of arrayPack.
dcl-s arrayPack packed(5) dim(5); dcl-s arrayZone zoned(7:2) dim(10); dcl-s i int(5); // Fill up arrayZone elements for i = 1 to %Elem(arrayZone); arrayZone(i) = i * 3.3333; EndFor; // Copy elements 5 - 9 of arrayZone to elements 1 - 5 of arrayPack arrayPack = %SubArr( arrayZone : 5 ); // Copy elements 1, 2 & 3 of arrayZone into elements 2, 3 & 4 of arrayPack %SubArr( arrayPack : 2 : 3 ) = arrayZone;
While On The Subject Of Arrays . . .
It may have slipped past you that in V7.1 we finally got the ability to perform lookup operations on Data Structure arrays. We have had DS arrays since V5R2, but to in order to search them we had to resort to APIs such as bsearch().
While bsearch still has a role for complex lookups, it is no longer needed for simple lookup operations. As of V7.1 we can perform lookups directly on DS arrays as in the example below. Notice that (*) is used as the array index to indicate that the entire array is to be searched:
dcl-s element int(5); dcl-s product char(7); dcl-ds productInfo Dim(999) Qualified; productCode Like(product); description Varchar(60); price Packed(7:2); cost Packed(7:2); end-ds; product = 'A1234456'; element = %LookUp( product : productInfo(*).productCode );
That’s a whole lot easier to code. The only downside is that currently only the basic %LookUp BIF is supported, not other members of the “family” such as %LookUpGE.
Jon Paris is one of the world’s foremost experts on programming on the IBM i platform. A frequent author, forum contributor, and speaker at User Groups and technical conferences around the world, he is also an IBM Champion and a partner at Partner400 and System i Developer. He hosts the RPG & DB2 Summit twice per year with partners Susan Gantner and Paul Tuohy.