Page 1 of 1

Arrays

Posted: Tue Feb 14, 2023 9:16 pm
by Zenerdiode
I’m missing having either a two dimensional array, or a way to dynamically address an array in OPL. For example, I know I can do:

Code: Select all

TEST:
LOCAL array1$(10,5),array2$(10,5),i%,j%
i%=1
array1$(i%)=2 :REM etc.
but how could I do:

Code: Select all

TEST:
LOCAL array1$(10,5),array2$(10,5),i%,j%
i%=1
j%=2
array(j%)$(i%)=2 :REM etc.
If you get what I mean…?

Array work-a-round

Posted: Wed Feb 15, 2023 1:25 am
by Martin
Hi Chris

You are correct there is no multi-dimensional arrays... OPL was created mainly as a file handling programming language. With that in mind I have in the past used the file handling capabilities as a way round the issue. It's long winded but something like...

Code: Select all

TEST:
LOCAL r%,v%,counter%,varible$(7)

REM CM/XP procedure use XTRAN on LZ

REM create test array
IF EXIST("A:TSTARY")
 DELETE "A:TSTARY"
ENDIF
CREATE "A:TSTARY",A,no%,value1$,value2$ :REM could have more value$'s
counter%=1

REM dimension test array file with 'blank' data (9 records)
DO
 A.no%=counter%
 A.value1$="-"   :REM would normally be empty strings ""
 A.value2$="-"   :REM but here use "-" to display later
 APPEND
 counter%=counter%+1
UNTIL counter%>9 :REM for his exercise limit no. of records to 9

REM Place values in the test array
WHILE varible$<>"Q"
 CLS
 REM Endless loop only way out is "Q"
 PRINT "String (Q=quit)" :INPUT varible$
 IF varible$="Q" : GOTO OUTPUT:: :ENDIF
 REM    1234567890123456
 PRINT "recrd 1 to 9 "; :INPUT r%
 PRINT "value 1 or 2 "; :INPUT v%
 
 FIRST
 DO
  IF A.no%=r% :REM no assignment will take place if r%>counter% OR v%>2
   IF v%=1 :A.value1$=varible$ :ENDIF
   IF v%=2 :A.value2$=varible$ :ENDIF
   UPDATE : BREAK
  ENDIF
  NEXT
 UNTIL EOF
ENDWH

OUTPUT::
REM Display values
FIRST
DO
 CLS
 REM    1234567890123456
 PRINT "Record No. ";A.no%
 PRINT LEFT$(A.value1$,7);CHR$(9);LEFT$(A.value2$,7)
 PAUSE -100
 KEY
 NEXT
UNTIL EOF
PRINT "all done.."
GET
I used something like it in my land surveying data logger, I remember I needed the 'array' to be in order so had to create a sort routine that I could call each time data was added or updated to sort the file back into order.. Note in that case make A.no% into A.no$ see page 2 of the Database Files Hints and Tips (here).

Always sincere
Martin

Re: Arrays

Posted: Wed Feb 15, 2023 10:59 am
by Zenerdiode
Thanks Martin, they say two great minds think alike, I had already tried to use temporary files to act as 2D arrays - but they were taking three seconds to open and using eight, the waiting time is excessive.

I have to rethink, I may just use many LOCALs and address them absolutely, but with that comes many IF statements.

Programming is our crossword or sudoku puzzles :D

Re: Arrays

Posted: Wed Feb 15, 2023 11:51 pm
by Lostgallifreyan
You know how arrays are constructed, so consider using the address like pointer arithmetic and access in C code. (Bear in mind that strings have their own weird un-C-like rules but can still be used in a similar way, it's just a fixed-length buffer you can do what you want with.)

My 'Bytes' program that allows placing arbitrary binary strings into locations various, while more than you need, suggests that a small bit of machine code to handle the read and write of an array by direct addressing is feasible, and likely fast too. You can do it OPL but there may be a tax on speed. PEEKB and PEEKW of ADDR(A%) act like BYTE and WORD casts respectively. Floating Point arrays would be harder to handle but there may well be a machine service that can do it, if called by ASM.

A variable is allocated space that your procedures own for their lifetime, and globals can be reached by sub-procedures, so you're free to access them any way you can so long as the result doesn't compromise OPL's ability to see them as originally intended when it needs to.

Re: Arrays

Posted: Fri Feb 17, 2023 10:07 pm
by MartinP
Zenerdiode,
I'm not that sure about what you mean by "dynamically address an array" but maybe you could use a flattened array? i.e. a 2d array stored in a 1d array, then just use a formula for the indices.
So for a 5x2 array, you'd use a 10 element 1d array, then for the indices use the formula: x%+(y%-1)*xx% where x% is the x index, and y% is the y index and xx% is the width of the array in the x direction (5 in this case).
This works for string or numeric arrays. You need to use the index formula every time you index the array, but this could be put into a function i:(x%,y%) to make the code look neater (although this will slow it down a bit).
See example below, which prints out a 2d array of words, most of which are "LO", but one says "HI". The width parameter xx% is made global so that it doesn't need to be passed to the index function every time.
If x% and y% are also made global, you wouldn't need to pass them either, but I prefer the look of it this way.
Martin P.

Code: Select all

array2d:
local x%,y%,yy%,array$(10,2)
global xx%
xx%=5 :yy%=2
x%=3 :y%=2
array$(i:(x%,y%))="HI"
x%=1 :y%=1
do
 do
  if array$(i:(x%,y%))=""
   array$(i:(x%,y%))="LO"
  endif
  print array$(i:(x%,y%)),
  x%=x%+1
 until x%=xx%+1
 print
 x%=1
 y%=y%+1
until y%=yy%+1
get

Code: Select all

i:(x%,y%)
RETURN x%+(y%-1)*xx%