Page 1 of 1

Dynamic Multi Arrays

Posted: Wed Jan 14, 2015 9:27 am
by PeterJ
I had the use of dynamic multi arrays (matrixes). Neither number of matrixes, nor their dimensions are known in the program (I use it to play with matrix arithmetic). As described in http://www.purebasic.fr/english/viewtop ... mic+Arrays it is possible to emulate a multi dimensional array in a one dimension array by calculating its position.
I think with the new features AllocateStructure and CopyArray there is an easier way:
1. Define a "dummy" matrix structure and allocate it

Code: Select all

 Structure Matrix
      Array.i(0,0)
 EndStructure
 *p.matrix=AllocateStructure(Matrix)
2. DIM a temporary array with the dimensions you want

Code: Select all

 rows=??
 cols=??
 Global DIM TempMatrix(rows,cols)
3. Copy "dimmed" Array to allocated array structure. COPYARRAY copies and changes the maximum row and column entries.

Code: Select all

 COPYARRAY(TempMatrix(),*p\Array())
 FreeArray(TempMatrix())
4. Now you can work with the matrix in the usal way

Code: Select all

 COPYARRAY(TempMatrix(),*p\Array())
 for i=0 to rows
     for j=0 to cols
        *p\array(i,j)=???
     next 
 next    
A tested sample of allocating dynamic arrays:

Code: Select all

; ----------------------------------------------------------------------------------------------
; Structure containing details of dynamically allocated Matrixes 
; ----------------------------------------------------------------------------------------------
Structure Matrix
  Name$
  rows.i
  cols.i
  *matPtr
EndStructure
;
; ----------------------------------------------------------------------------------------------
; Structure of dynamically allocated Matrixes 
; ----------------------------------------------------------------------------------------------
Structure MatArray  
  Array matrix.i(0,0)
EndStructure
;
; ----------------------------------------------------------------------------------------------
; Allocate MAP containg details of allocated Matrixes 
; ----------------------------------------------------------------------------------------------
Global NewMap Matrix.Matrix()
;
; ----------------------------------------------------------------------------------------------
; Create Dynamic Matrix
; ----------------------------------------------------------------------------------------------
Procedure MatrixCreate(name$,rows,cols) 
Protected Dim TempMatrix(rows,cols)           ; Create temporary Matrix with requested rows and columns
Protected *matx.MatArray  
name$=UCase(name$)  
*matx=AllocateStructure(matArray)             ; Create target Matrix dimension(0,0) 
CopyArray(TempMatrix(),*matx\matrix())        ; CopyArray overrides dimensions of target array
Matrix(name$)\Name$=UCase(name$)              ; Save array information in Map
Matrix(name$)\rows=rows
Matrix(name$)\cols=cols
Matrix(name$)\matPtr=*matx
FreeArray(TempMatrix())                       ; Free temporary Matrix, no longer needed 
ProcedureReturn *matx                         ; return pointer to created Matrix 
EndProcedure 
;
; ----------------------------------------------------------------------------------------------
; Get Pointer to any dynamic Matrix
; ----------------------------------------------------------------------------------------------
Procedure MatrixADDR(name$) 
Protected *matptr.matArray  
name$=UCase(name$) 
*matptr=Matrix(name$)  
If *Matptr=0 
   ProcedureReturn -1
EndIf 
ProcedureReturn Matrix(name$)\matPtr
EndProcedure 
;
; ----------------------------------------------------------------------------------------------
; Debug requested Matrix 
; ----------------------------------------------------------------------------------------------
Procedure MatrixDebug(name$)
Protected *matptr.matArray, rows, cols, *m.Matarray, line$, i, j 
Debug "Matrix "+name$  
name$=UCase(name$) 
*matptr=Matrix(name$)
If *Matptr=0 
   ProcedureReturn -1
EndIf 
rows=Matrix(name$)\rows
cols=Matrix(name$)\cols
*m=Matrix(name$)\matPtr
For i=1 To rows
    line$=""
    For j=1 To cols
        Line$+RSet(Str(*m\matrix(i,j)),6,"0")+"  "
    Next 
    Debug "("+RSet(Str(i),4)+")   "+line$
Next  
EndProcedure
;
; ----------------------------------------------------------------------------------------------
; Main Program 
; ----------------------------------------------------------------------------------------------
;
Define *m.Matarray, i, j, ct 
;
MatrixCreate("Fred",47,21)       ; Create Matrix "Fred", with it required dimensions
MatrixCreate("Annie",10,10)      ; Create Matrix "Annie" ... 
;
*m=MatrixADDR("Fred")            ; Get Pointer to "Fred"
For i=1 To 47                    ; Set Matrix with Values
    For j=1 To 21
        ct+1
        *m\matrix(i,j)=ct
    Next 
Next   
;
*m=MatrixADDR("Annie")           ; Get Pointer to "Annie"
For i=1 To 10                    ; Set Matrix with Values
    For j=1 To 10
        *m\matrix(i,j)=i+j
    Next 
Next   
; Debug both Matrixes 
MatrixDebug("Fred")              ; Debug Matrix "Fred" 
MatrixDebug("Annie")             ; Debug Matrix "Annie"
 

Re: Dynamic Multi Arrays

Posted: Wed Jan 14, 2015 5:03 pm
by Demivec
PeterJ wrote: As described in http://www.purebasic.fr/english/viewtop ... mic+Arrays it is possible to emulate a multi dimensional array in a one dimension array by calculating its position.
I think with the new features AllocateStructure and CopyArray there is an easier way:
@peter: The subject in the thread you linked to was how to ReDim a multi-dimensional array by something other than its last dimension and keep it's contents.

You haven't actually done that. You've only created a new array with the desired dimensions (and later filled it).

The method you describe here can be accomplished much easier by doing this:

Code: Select all

*matx.MatArray   = AllocateStructure(matArray)     ; Create target Matrix dimension(0,0) 
Dim *matx\matrix(rows, cols)                       ; Create temporary Matrix with requested rows and columns

I also noticed that when I execute the code sample you provided with the debugger it ends unexpectedly with an error message as it is attempting to debug the array 'Annie'. It stops at different points in each run, sometimes after the 3 row, others the 5 or even after all 10 lines are shown. Something is not functioning correctly. I corrected some minor functional errors in the code but wasn't able to resolve it completely. It may be evidence of something more serious going wrong.

Re: Dynamic Multi Arrays

Posted: Wed Jan 14, 2015 7:04 pm
by srod
I am guessing that InitializeStructure() will fix it. :)

Re: Dynamic Multi Arrays

Posted: Wed Jan 14, 2015 9:52 pm
by netmaestro
srod wrote:I am guessing that InitializeStructure() will fix it.
PB Doc wrote: Syntax

*Item.StructureName = AllocateStructure(StructureName)

Description

Allocates a new dynamic structure item. This dynamic structure item is properly initialized and ready to use, without the need to call InitializeStructure().
Why couldn't you have said you were betting that InitializeStructure() would fix it? I need the money!

Re: Dynamic Multi Arrays

Posted: Wed Jan 14, 2015 10:05 pm
by srod
Ah, now when did AllocateStructure() get added? That's what happens when you don't pay attention for a year or so! :)

What else have I missed?

Re: Dynamic Multi Arrays

Posted: Wed Jan 14, 2015 10:27 pm
by netmaestro
I don't think there was anything else. Just CreateShitlessDog(<breed>) which at first seemed good, you didn't have to scoop but the bugs forum was flooded with complaints about uncontrollable fleas and Fangbeast wouldn't leave them alone so I think it's been dropped.

Re: Dynamic Multi Arrays

Posted: Wed Jan 14, 2015 11:59 pm
by netmaestro
This seems working ok, both axes can be redimmed to any size:

Code: Select all

Structure column
  Array col.i(0)
EndStructure

Structure Matrix
  Array row.column(0)
EndStructure

Declare Get(*matrix.Matrix, x, y)
Declare Set(*matrix.Matrix, x, y, value)

*this.Matrix = AllocateStructure(Matrix)

ReDim *this\row(10)
For i=0 To 9
  ReDim *this\row(i)\col(10)
Next

cc=0
For i=0 To ArraySize(*this\row())-1
  For j=0 To ArraySize(*this\row(i)\col())-1
    Set(*this, i, j, cc)
    cc+1
  Next
Next

For i=0 To ArraySize(*this\row())-1
  For j=0 To ArraySize(*this\row(i)\col())-1
    Debug "element "+ Str(i) + "," + Str(j) + " = " + Get(*this, i, j)
  Next
Next

Procedure Get(*matrix.Matrix, x, y)
  ProcedureReturn *matrix\row(x)\col(y)
EndProcedure

Procedure Set(*matrix.Matrix, x, y, value)
  *matrix\row(x)\col(y) = value
EndProcedure