18 And yet more programming tips
::::::
W
hy all these exercises that most possibly will never be used under T24? I think it’s good to know your main work tool – especially what it can do and what it can’t. I can give you the following example: once I was given a code that split a long line into several ones with smaller length (nothing complex like keeping words’ bounds – just cut the line).That code implemented a loop that cut pieces and put them to dynamic array one by one.
Having seen that, I asked: why not use fmt()? See:
fmtsample.b
1 * FMT() in string cutting
2
3 V.IN = ’This is quite a long line that we have to break into parts’ \
4 : ’ with length of 35 characters while not thinking about cutting’ \
5 : ’ a word in two in the very middle. So we are going to use FMT()’ \
6 : ’ for this tremendous task’
7 V.OUT = FMT(V.IN, ’35L’)
8 CHANGE @TM TO @FM IN V.OUT
9
10 V.CNT = DCOUNT(V.OUT, @FM)
11 FOR V.I = 1 TO V.CNT
jsh ~ --> fmtsample
Line 1 is: This is quite a long line that we h Line 2 is: ave to break into parts with length Line 3 is: of 35 characters while not thinkin Line 4 is: g about cutting a word in two in th Line 5 is: e very middle. So we are going to u Line 6 is: se FMT() for this tremendous task
Unfortunately it’s not in manuals that if you use fmt() to format a string “padding” it to less width than it actually has you’ll end up with that string delimited with text marks (@tm, or ASCII 251). I came to that looking at some strange output in a report where a programmer issued an fmt() for a longer string and strange characters appeared as a result.
18 AND YET MORE PROGRAMMING TIPS
I happened to have two different consultants to give me a chunk of code doing the same things as one-two lines can do. One of them – after seeing my proposition – said: “didn’t know that, will use it from now on”. Another told me “why should I care since my code works?” So here you see the difference between T24 consultant and good T24 consultant.
Another issue. Once upon a time I had a discussion with my colleagues about some recommendations in the Temenos document describing programming standards. Among other things, it says (quoting from my memory): “use remove...from to retrieve items from a list in a loop, it’s faster than extract each element using for...next”. I then made a test to see if it’s true. But firstly we need to see where we can obtain a list which isn’t very small. So:
In search of a big SELECT C:\sa-tafc> jrunT24.cmd
jsh ~ --> COUNT FBNK.CUSTOMER 423 Records counted
Oops... what about number of accounts?
Still in search of a big SELECT...
jsh ~ --> COUNT FBNK.ACCOUNT 3620 Records counted
Still not enough... We’ll then use a trick - put all the fields of every ACCOUNT record into the select list:
BSELECT helps us jsh ~ --> BSELECT FBNK.ACCOUNT
774007 Records selected
>
Well, much better now. Here goes the code of our test:
retritem.b
1 * Test of array retrieval speed
2
3 COMMON /MY.COMMON/ V.RUN.NR
4 V.RUN.NR ++
5 IF V.RUN.NR GT 1 THEN
18 AND YET MORE PROGRAMMING TIPS
6 CRT ’Try again a a new session’
7 STOP
8 END
Here we use named common to understand if this program was invoked in a “fresh”
session – to avoid inaccuracy of results caused by, say, cache etc. But before continuing, let’s test this approach first. Just add stop...end after that code (it probably will work and without that but it’s better to keep formalities). Compile and run it:
COMMON area test C:\sa-tafc> jrunComp.cmd retritem
retritem.c
C:\sa-tafc>jrunT24.cmd jsh ~ --> retritem 1 jsh ~ --> retritem 1 jsh ~ -->
No it doesn’t tell us that we need to run it in a new session. Obviuosly common area wasn’t preserved. Why? My colleague already mentioned here a few times told me that:
...jcompile always creates 2 executables (.so, .el for Unix/Linux and .exe, .dll for Windows). In our case, if .dll presents then it’s being loaded into memory, thus giving us the ability to store the common area. If there’s no .dll, .exe is executed but then nothing – neither common nor chain sequence – can be stored. In addition, jBASE uses environment variable %path% to find a .dll so we need the path to our work directory to present there.
Yes, I noticed that without .dll chain didn’t work as expected – the execution returned to “parent” program after “child” has finished...
Firstly let’s amend our compilation command file so it no more deletes .dll file after compilation:
More changes in jrunComp.cmd
34 jcompile -I <path_to_bnk.run_parent_directory>\T24.BP %1.b && del %1.obj
18 AND YET MORE PROGRAMMING TIPS
We need then to correct the %path% in jrunT24.cmd and jrunSH.cmd:
More changes in %PATH%
20
21 set PATH=%COMPILER_HOME%\bin;%TAFC_HOME%\bin;C:\windows\system32\;%HOME%
Now:
COMMON area test C:\sa-tafc> jrunComp.cmd retritem
retritem.c
C:\sa-tafc> jrunT24.cmd jsh ~ --> retritem 1 jsh ~ --> retritem 1 Try again a a new session jsh ~ -->
OK now. By the way, why we’ve just increased the value of v.run.nr and didn’t get an error? So it was 0 from the beginning? And where is it specified?
Here we touch an area that is called “emulation”. For T24 “prime” emulation is used.
Have you noticed an environment variable JBCEMULATE being set to “prime”? Take a look into configuration file; the setting that interests us is “named common”:
jBASE emulation settings - prime C:\sa-tafc> jrunSH.cmd
jsh ~ --> CT <path_to_TAFC>\config Config_EMULATE ...
192 #
193 # Emulation for Prime.
194 # 195 prime:
196 .dup = jbase ...
204 .named_common = zero
What if we hadn’t set up that environment variable? See the default:
jBASE default emulation settings 009 jbase:
010 default:
...
028 .named_common = null
You can find decriptions of emulation settings in the file Config EMULATE.txt which is located in the same directory as Config EMULATE. For example:
18 AND YET MORE PROGRAMMING TIPS
Settings for named common named_common = unassigned|null|zero
Shows how to set named common when first referenced. Default is unassigned.
’setting’ can be :
"zero" to initialise to numeric 0
"unassigned" to keep it as an unassigned variable
"null" to create it as a zero length null string.
So under different emulations our program will have different behaviour since we’re not able to add 1 to an unassigned variable or a null without getting an error – or, in fact, we are since error messages are successfully suppressed by our current setup which is typical for T24.
There are certain following environment variables – namely, jbase errmsg zero used and jbase errmsg non numeric – which control jBASE behaviour in such cases. For example, we have them being set to suppress error messages and not enter debugger. There is a self-explanatory one – jbase errmsg divide by zero – as well.
To see how they work we’ll tackle them and try to violate some rules. Example 1:
testnull.b
1
2 V.RUN.NR ++
3 CRT V.RUN.NR
4
5 STOP
6 END
testnull run C:\sa-tafc> jrunComp.cmd testnull
testnull.c
C:\sa-tafc> jrunEXE.cmd testnull.exe 1
So far, so good. Add some control (using putenv() to change the environment):
testnull.b - changed
1
2 IF NOT(PUTENV(’JBASE_ERRMSG_ZERO_USED=0’)) THEN
3 CRT ’PUTENV failed’
4 STOP
5 END
6
7 V.RUN.NR ++
18 AND YET MORE PROGRAMMING TIPS
8 CRT V.RUN.NR
9
10 STOP
11 END
12
testnull run C:\sa-tafc> jrunComp.cmd testnull
testnull.c
C:\sa-tafc>jrunEXE.cmd testnull.exe
Invalid or uninitialised variable -- NULL USED , Var V.RUN.NR , Line 7 , Source testnull.b 1
Example 2. Add a number to a string:
testnull.b
1
2 V.RUN.NR = ’ABC’
3 V.RUN.NR ++
4 CRT V.RUN.NR
5
6 STOP
7 END
8
testnull run C:\sa-tafc> jrunComp.cmd testnull
testnull.c
C:\sa-tafc> jrunEXE.cmd testnull.exe 1
Looks OK – but only looks so. Add the control now:
testnull.b - changed
1
2 IF NOT(PUTENV(’JBASE_ERRMSG_NON_NUMERIC=0’)) THEN
3 CRT ’PUTENV failed’
4 STOP
5 END
6
7 V.RUN.NR = ’ABC’
8 V.RUN.NR ++
9 CRT V.RUN.NR
18 AND YET MORE PROGRAMMING TIPS
10
11 STOP
12 END
13
testnull run C:\sa-tafc> jrunComp.cmd testnull
testnull.c
C:\sa-tafc> jrunEXE.cmd testnull.exe Non-numeric value -- ZERO USED ,
Variable ’(UNKNOWN)’ , Line 8 , Source testnull.b 1
See the difference? I’d strongly recommend to test your programs with these variables set to 0.
Back to out task – in case you haven’t forgotten yet what it was. And it was – to see which method used for retrieving an array element is faster – remove or for...next. Here’s the code (I repeat the first part – that checks if this program was run already – to refresh your memory):
retritem.b
1 * Test of array retrieval speed
2
3 COMMON /MY.COMMON/ V.RUN.NR
4 V.RUN.NR ++
5 IF V.RUN.NR GT 1 THEN
6 CRT ’Try again a a new session’
7 STOP
8 END
Then we get the method being used this time (passed as first parameter, defaults to 1):
retritem.b
9
10 V.METHOD = SENTENCE(1)
11 IF V.METHOD NE 2 THEN V.METHOD = 1
Now it’s time for bselect to get a decent SELECT list:
retritem.b
12
13 EXECUTE ’BSELECT FBNK.ACCOUNT’ RTNLIST V.BIG.L
14
18 AND YET MORE PROGRAMMING TIPS
Unfortunately – as a quick test showed – that list wasn’t still long enough, to expand it just add it to itself 5 times:
retritem.b
15 V.CNT = 5
16 FOR V.I = 1 TO V.CNT
17 V.BIG.L := @FM : V.BIG.L
18 NEXT V.I
Time to start; method #1...
retritem.b
19
20 V.STRT = TIME()
21
22 IF V.METHOD EQ 1 THEN
23
24 LOOP
25 REMOVE V.ID FROM V.BIG.L SETTING V.STATUS
26 IF V.STATUS EQ 0 THEN BREAK
27 REPEAT
28
...and #2, then print the result:
retritem.b
29 END ELSE
30
31 V.CNT = DCOUNT(V.BIG.L, @FM)
32
33 FOR V.I = 1 TO V.CNT
34 V.ID = V.BIG.L<V.CNT>
35 NEXT V.I
Compile, run - and see that for...next wins:
retritem run C:\sa-tafc> jrunComp.cmd retritem
18 AND YET MORE PROGRAMMING TIPS
retritem.c
C:\sa-tafc> jrunT24.cmd jsh ~ --> retritem 1
774007 Records selected 10
jsh ~ --> exit
C:\sa-tafc> jrunT24.cmd jsh ~ --> retritem 2
774007 Records selected 5
jsh ~ -->
Not sure if other platforms give the same result – you can try it yourself to see. You might see “0” as a result for both methods – in this case your computer is faster than that virtual machine of mine (almost 100% sure that it is). Then just increase the array being tested.
Last for this chapter – can’t resist to put here “<-1>” usage note. You might have said that in the line 17 of retritem.b I could have used the following syntax:
retritem.b
17 V.BIG.L<-1> = V.BIG.L
Why I’m not the big fan of “<-1>”? Not only it might be slower (as some sources say – especially when adding subvalues) but it also works a bit differently from my chosen method.
See:
additem.b
1
2 V.ARR = ’’
3 V.ARR<-1> = ’’
4 V.ARR<-1> = ’’
5 V.ARR<-1> = ’A’
6 V.ARR<-1> = ’’
7 V.ARR<-1> = ’B’
8 V.ARR<-1> = ’’
9 V.ARR<-1> = ’C’
10
11 V.ARR.2 = ’’
18 AND YET MORE PROGRAMMING TIPS
12 V.ARR.2<1> = ’’
13 V.ARR.2<2> = ’’
14 V.ARR.2<3> = ’A’
15 V.ARR.2<4> = ’’
16 V.ARR.2<5> = ’B’
17 V.ARR.2<6> = ’’
18 V.ARR.2<7> = ’C’
19
20 CHANGE @FM TO ’^’ IN V.ARR
21 CHANGE @FM TO ’^’ IN V.ARR.2
22
23 CRT V.ARR
24 CRT V.ARR.2
25
26 STOP
27 END
28
Before you compile and run it, ask yourself: will the output be the same?
additem run C:\sa-tafc> jrunComp.cmd additem
additem.c
C:\sa-tafc> jrunEXE.cmd additem A^^B^^C
^^A^^B^^C
I know I hadn’t any empty values at the start for my last test but in other cases it could be different so I just always use the safer method.