Posts: 2,698
Threads: 328
Joined: Apr 2022
Reputation:
218
(06-20-2024, 09:16 PM)12centuries Wrote: One question I have: how does QB64 store the LBOUNDS and UBOUNDS for an array? Could you point me to the data structure in source?
It seems that QB wants to keep all of the data in the array and just change the indexes that point to the data. Is that true? I'm not sure I understand the reasoning behind that, but I'd love to know.
The data structure is just an array. The bounds are stored in some of the array entries.
From our source in libqb.cpp:
Code: (Select All) ptrszint func_lbound(ptrszint *array, int32 index, int32 num_indexes) {
if ((index < 1) || (index > num_indexes) || ((array[2] & 1) == 0)) {
error(9);
return 0;
}
index = num_indexes - index + 1;
return array[4 * index];
}
ptrszint func_ubound(ptrszint *array, int32 index, int32 num_indexes) {
if ((index < 1) || (index > num_indexes) || ((array[2] & 1) == 0)) {
error(9);
return 0;
}
index = num_indexes - index + 1;
return array[4 * index] + array[4 * index + 1] - 1;
}
Posts: 3,985
Threads: 178
Joined: Apr 2022
Reputation:
222
(06-20-2024, 09:52 PM)12centuries Wrote: WOW! I walked away for a bit and found a flurry of solutions.
All while I was working on my own solution... heh.
Code: (Select All)
SUB Resize2DArray (Array(), yLowerNew, yUpperNew, xLowerNew, xUpperNew)
'
' Redimension a two-dimensional array by altering the upper
' and/or lower bounds of either dimension.
'
' The new array's existing elements will be in the
' same (x,y) position as they were previously.
'
' If reducing an array dimension, DATA LOSS WILL OCCUR.
'
'
DIM y, x
DIM yLowerCurrent, yUpperCurrent, xLowerCurrent, xUpperCurrent
DIM yLower, xLower, yUpper, xUpper
' start by getting our current array boundaries
yLowerCurrent = LBOUND(Array, 1)
yUpperCurrent = UBOUND(Array, 1)
xLowerCurrent = LBOUND(Array, 2)
xUpperCurrent = UBOUND(Array, 2)
' do we actually require a resize?
IF yLowerNew = yLowerCurrent AND xLowerNew = xLowerCurrent AND yUpperNew = yUpperCurrent AND xUpperNew = xUpperCurrent THEN
EXIT SUB
END IF
' Find the smaller of the lower bounds and the larger of
' the upper bounds.
yLower = (yLowerNew + yLowerCurrent + ABS(yLowerNew - yLowerCurrent)) / 2
xLower = (xLowerNew + xLowerCurrent + ABS(xLowerNew - xLowerCurrent)) / 2
yUpper = (yUpperNew + yUpperCurrent - ABS(yUpperNew - yUpperCurrent)) / 2
xUpper = (xUpperNew + xUpperCurrent - ABS(xUpperNew - xUpperCurrent)) / 2
' create a temporary array with the updated dimensions
DIM Temp(yLowerNew TO yUpperNew, xLowerNew TO xUpperNew)
' Copy original array values to new array
FOR y = yLower TO yUpper
FOR x = xLower TO xUpper
Temp(y, x) = Array(y, x)
NEXT
NEXT
' redimension original array
REDIM Array(yLowerNew TO yUpperNew, xLowerNew TO xUpperNew)
' copy temporary values back to original array
FOR y = yLower TO yUpper
FOR x = xLower TO xUpper
Array(y, x) = Temp(y, x)
NEXT
NEXT
END SUB
i recommend you put the x in first position and y in second, (x, y) all graphics commands work that way.
the only thing that doesn't is locate row, column which is why i am in habit or saying row, column for locate.
easy to remember alphabetically x comes before y.
b = b + ...
Posts: 303
Threads: 10
Joined: Apr 2022
Reputation:
44
(06-20-2024, 09:16 PM)12centuries Wrote: It seems that QB wants to keep all of the data in the array and just change the indexes that point to the data. Is that true? I'm not sure I understand the reasoning behind that, but I'd love to know. In a lot of ways it's just laziness, it's the easiest way to implement _preserve because it requires no special copying logic. The data backing an array is always stored as one big one-dimensional array, even for multi-dimensional arrays, and that backing data is simply copied verbatim into the memory for the resized array. When the array is accessed, QB64 does logic like `x * bound1 + y * bound2 + z` to calculation the location of the `x,y,z` index into that backing one-dimensional array. If the size of `bound1` or `bound2` are changed then that throws off all the math for how the `x,y,z` indexes correspond to the backing data, even though the backing data was never changed.
That said, from a technical sense _preserve could maintain the index locations, it's just more complicated to implement.
Posts: 34
Threads: 4
Joined: Apr 2022
Reputation:
14
06-21-2024, 09:35 AM
(This post was last modified: 06-21-2024, 09:38 AM by luke.)
It's an implementation detail and not accessible from within BASIC code, but the array descriptor data structure is thus:
Code: (Select All) a[0]: pointer to data
a[1]: reserved
a[2]: Flags (1 = init, 2 = static, 4 = cmem)
a[3]: reserved
a[4]: lower bound <- rightmost dimension
a[5]: number of elements
a[6]: Block size
a[7]: reserved
a[8]: lower bound
a[9]: number of elements
a[10]: Block size
a[11]: reserved
a[12]: lower bound <- leftmost dimension
a[13]: number of elements
a[14]: 1
a[15]: reserved
a[last]: _MEM lock
Where the central group of 4 entries repeats for each dimension. Block size is a stride value that takes into account the dimensions to the left. Reserved denotes fields that as far as I can tell are never used.
Posts: 60
Threads: 8
Joined: Apr 2022
Reputation:
5
(06-20-2024, 02:07 AM)SMcNeill Wrote: (06-19-2024, 10:36 PM)TerryRitchie Wrote: Yep, this a known issue. I'll let Steve explain it in detail though. He explained it to me a few years back when I was wondering the same thing but I can't seem to find that explanation now? It may have been on a previous forum.
(If not in a SUB/Function which cleans up variables at exit, you may want to REDIM Temp(0,0) AS TYPE to reduce memory usage and clean up that temp array to as small a footprint as possible.)
Perhaps we need a a keyword such as
_ERASE(Temp)
that cleans the array out memory completely. I remember having that keyword back in 8-bit MS Basic.
It's not the having, it's the doing.
Posts: 10
Threads: 3
Joined: Jun 2024
Reputation:
2
(06-20-2024, 10:57 PM)Kernelpanic Wrote: The manual for Quick- and QBasic: "Neither the type nor the dimension of an array may be changed."
I don't know what you're doing, but I don't think it's solid.
Quote:When reducing an array dimension, DATA LOSS OCCURS.
Well, what do you know! If I reduce a 5X5 array to 4X4, data is lost... Thanks!
Let's see where this ends up! Quite right, QBasic and QuickBasic were much more limited than even PDS/QBX were, which allowed the size of dimensions to be changed: "Although you can change the size of an array's dimensions with the REDIM statement, you can not change the number of dimensions." ( Microsoft BASIC Professional Development System 7.1 — Basic Language Reference, 1989, p. 289).
So many languages have the ability to change array dimensions, like python's del statement, javascript's slice method, the Arrays.copyOf method in Java, or std::vector's resize method in C++. It's too bad Q[uick]Basic didn't have something. I think the _PRESERVE keyword is a credit to the QB64 developers for including it.
I suppose that warning was a bit obvious, It's a long-ingrained habit of mine to ensure that any potential loss of data is fully documented so that anyone who reuses my code would have a full grasp of the consequences.
So here's what I'm doing...this code is part of a game with multiple levels that are loaded from user-editable text files. The dimensions of the maps are not known until read, and rather than read through the file twice (first a parsing read, then array dimensioning, then a loading read), I opted to use a more adaptive approach which adjusts the array size as it loads.
When all is done, the array is sometimes larger than required, so I reduce it. While it's not intended to pare down existing data from an array, you could use it for that, so I think the warning is warranted.
Posts: 1,000
Threads: 50
Joined: May 2022
Reputation:
27
@12centuries - I have checked again now, there is no resizing in C either - but in C you can program almost everything yourself if you can do it. Does it make sense?
Regarding "std::vector's resize", the dimension is not changed, or am I misunderstanding that? Only the size of the array is changed. This also works with Basic64.
Code: (Select All)
'Zweidimensionales Feld mit Redim neu dimensionieren - 29. Dez. 2022
$Console:Only
Option _Explicit
Option Base 1
Dim As Integer neuDimensionZeile, neuDimensionSpalte
Dim As Integer zeilenDim, spaltenDim
Dim As Integer a, b, i, j, y, z
Locate 2, 2
Input "Feldimension Zeilen : ", zeilenDim
Locate 3, 2
Input "Felddimension Spalten: ", spaltenDim
'Feld mit Vorgaben initialisieren
Dim As Integer zweiDimfeld(zeilenDim, spaltenDim)
Locate CsrLin + 2, 2
z = 1
For i = 1 To zeilenDim
For j = 1 To spaltenDim
zweiDimfeld(i, j) = z
Print Using "## "; zweiDimfeld(i, j),
z = z + 1
Next
Print: Locate , 2
Next
'Vor Neudimensionierung Speicher freigeben. Ist bei Anwendung
'von REDIM nicht noetig, da dieser ERASE + DIM zusammenfasst - S.188
'Erase zweiDimfeld
'Feld neu dimensionieren
Locate CsrLin + 2, 2
Input "Neue Feldimension Zeile : ", neuDimensionZeile
Locate CsrLin + 0, 2
Input "Neue Feldimension Spalte: ", neuDimensionSpalte
ReDim zweiDimfeld(neuDimensionZeile, neuDimensionSpalte)
Locate CsrLin + 2, 2
y = 1
For a = 1 To neuDimensionZeile
For b = 1 To neuDimensionSpalte
zweiDimfeld(a, b) = y
Print Using "## "; zweiDimfeld(a, b),
y = y + 1
Next
Print: Locate , 2
Next
Locate CsrLin + 3, 2
End
|