Subscript out of range on Class - arrays

I created this class named "CInventory". This class generates an array of "valid data". To see this array on a range I tried to "paste" the array to see the result. I get a "subscript out of range". The error appears on line 6 (cinv.AllocateInventoryData) of the simple module.
This is the Class Module called CInventory:
Option Explicit
Private Path As String
Private TextFileNumber As Integer
Private InventoryData() As String
Public Property Get GetInventoryData() As String
GetInventoryData = InventoryData
End Property
Public Property Let SPath(Text As String)
Path = Text
End Property
Public Sub AllocateInventoryData()
Dim ColumnArray() As String
TextFileNumber = FreeFile
Dim FileContent As String
Dim i, j As Integer
j = 0
Open Path For Input As TextFileNumber
FileContent = Input(LOF(TextFileNumber), TextFileNumber)
Close TextFileNumber
ColumnArray() = Split(FileContent, vbCrLf)
For i = LBound(ColumnArray) To UBound(ColumnArray)
If IsValidRow(ColumnArray(i)) Then
ReDim Preserve InventoryData(j, 4)
InventoryData(j, 0) = Left(ColumnArray(i), 8)
InventoryData(j, 1) = Trim(Mid(ColumnArray(i), 10, 8))
InventoryData(j, 2) = Trim(Mid(ColumnArray(i), 19, 18))
InventoryData(j, 3) = Trim(Mid(ColumnArray(i), 57, 2))
InventoryData(j, 4) = Trim(Mid(ColumnArray(i), 60, 12))
j = j + 1
End If
Next i
End Sub
Private Function IsValidRow(Text As String) As Boolean
If Left(Text, 6) = "iclorp" Or Left(Text, 5) = "Page:" Or Len(Trim(Text)) < 3 _
Or Left(Text, 4) = "Site" Or Left(Text, 6) = "------" Then
IsValidRow = False
Else
IsValidRow = True
End If
End Function
And this is the Module from which I call the class CInventory (simple module)
Option Explicit
Sub example()
Dim cinv As CInventory
Set cinv = New CInventory
cinv.SPath = "H:\Joel\invent_2.prn"
cinv.AllocateInventoryData '<-- here appears the error
Worksheets(1).Cells(1, 1) = cinv.GetInventoryData
End Sub

here is the answer!, after a while but it works as I wanted
The class module 'CInventory'
Option Explicit
Private Path As String
Private TextFileNumber As Integer
Private InventoryData() As String
Private UBoundInventoryDataRows As Integer
Private UBoundInventoryDataColumns As Integer
Public Property Get GetUBoundInventoryDataRows() As Integer
GetUBoundInventoryDataRows = UBoundInventoryDataRows
End Property
Public Property Get GetUBoundInventoryDataColumns() As Integer
GetUBoundInventoryDataColumns = UBoundInventoryDataColumns
End Property
Public Property Get GetInventoryData() As String()
GetInventoryData = InventoryData
End Property
Public Property Let SPath(Text As String)
Path = Text
End Property
Public Sub AllocateInventoryData()
Dim tempArray() As String
Dim TempLineArray() As String
Dim ColumnArray() As String
Dim FileContent As String
Dim i, j As Integer
TextFileNumber = FreeFile
j = 0
Open Path For Input As TextFileNumber
FileContent = Input(LOF(TextFileNumber), TextFileNumber)
Close TextFileNumber
ColumnArray() = Split(FileContent, vbCrLf)
For i = LBound(ColumnArray) To UBound(ColumnArray)
If LCase(Left(Trim(ColumnArray(i)), 2)) = "mx" Then
ReDim Preserve tempArray(j)
tempArray(j) = Trim(Left(ColumnArray(i), 9)) & ":" & _
Trim(Mid(ColumnArray(i), 10, 9)) & ":" & _
Trim(Mid(ColumnArray(i), 19, 18)) & ":" & _
Trim(Mid(ColumnArray(i), 37, 20)) & ":" & _
Trim(Mid(ColumnArray(i), 57, 4)) & ":" & _
Trim(Mid(ColumnArray(i), 61, 12)) & ":" & _
Trim(Mid(ColumnArray(i), 73, 9)) & ":" & _
Trim(Mid(ColumnArray(i), 82, 9)) & ":" & _
Trim(Mid(ColumnArray(i), 91, 8)) & ":" & _
Trim(Mid(ColumnArray(i), 99, 6)) & ":" & _
Trim(Mid(ColumnArray(i), 105, 9)) & ":" & _
Trim(Mid(ColumnArray(i), 114, 6)) & ":" & _
Trim(Mid(ColumnArray(i), 120, 4)) & ":" & _
Trim(Mid(ColumnArray(i), 124, 3))
j = j + 1
End If
Next i
'in a normal module we shall transpose this next array.
'I did this in this way because of redim agreement
' "the last dimension only could be re-dimensioned"
ReDim InventoryData(13, UBound(tempArray))
For i = LBound(tempArray) To 13
For j = LBound(tempArray) To UBound(tempArray)
TempLineArray = Split(tempArray(j), ":")
InventoryData(i, j) = TempLineArray(i)
Next j
Next i
UBoundInventoryDataRows = UBound(InventoryData, 1)
UBoundInventoryDataColumns = UBound(InventoryData, 2)
End Sub
and here the simple module
Option Explicit
Sub example()
Dim cinv As CInventory
Set cinv = New CInventory
cinv.SPath = "H:\Joel\invent_2.prn"
cinv.AllocateInventoryData
Dim row, column As Integer
row = cinv.GetUBoundInventoryDataRows
column = cinv.GetUBoundInventoryDataColumns
Dim Destination As Range
Set Destination = Range("a1")
Destination.Resize(column, row).Value = Application.Transpose(cinv.GetInventoryData)
Set cinv = Nothing
End Sub

Related

VBA - MsgBox a 2D Array (Matrix)

I am trying to visualize a 2D Matrix (Array) using a MsgBox, but the code I have doesn't give me the correct representation.
Sub test()
Dim M(22, 7) As Double
TwoD_Array_Matrix_In_MSGBOX (M)
End Sub
'_________________________________________________________________________
Public Function TwoD_Array_Matrix_In_MSGBOX(arr As Variant)
h = UBound(arr, 1)
w = UBound(arr, 2)
'MsgBox ("h = " & CStr(h + 1) & vbCrLf & "w = " & CStr(w + 1)) ' to check if the width and hight of the Matrix are correct
Dim msg As String
For i = 0 To w
For ii = 0 To h
msg = msg & arr(ii, i) & vbTab
Next ii
msg = msg & vbCrLf
Next i
MsgBox msg
End Function
This is the result I get:
You have w and h interchanged.
Dim msg As String
For i = 0 To h
For ii = 0 To w
msg = msg & arr(i, ii) & vbTab
Next ii
msg = msg & vbCrLf
Next i
MsgBox msg
this works perfectly for me
Private Sub this()
Dim this(22, 7) As Integer
Dim msg$
For i = LBound(this, 1) To UBound(this, 1)
For j = LBound(this, 2) To UBound(this, 2)
msg = msg & this(i, j) & vbTab
Next j
Next i
MsgBox msg
End Sub
It might be more flexible to write a function which returns a string, a sort of 2-dimensional join, which allows you to choose both the item delimiter (defaulting to vbTab) and the row delimiter (defaulting to vbCrLf).
You can MsgBox this string -- or write it to the immediate window -- or (with a comma chosen as one of the delimiters) -- write it to a CSV file, etc.:
Function MatrixJoin(M As Variant, Optional delim1 As String = vbTab, Optional delim2 As String = vbCrLf) As String
Dim i As Long, j As Long
Dim row As Variant, rows As Variant
ReDim rows(LBound(M, 1) To UBound(M, 1))
ReDim row(LBound(M, 2) To UBound(M, 2))
For i = LBound(M, 1) To UBound(M, 1)
For j = LBound(M, 2) To UBound(M, 2)
row(j) = M(i, j)
Next j
rows(i) = Join(row, delim1)
Next i
MatrixJoin = Join(rows, delim2)
End Function
Tested by:
Sub test()
Dim A As Variant
A = Range("A1:B3").Value
MsgBox MatrixJoin(A)
Debug.Print MatrixJoin(A, ",", ";")
End Sub
Screenshots of output:

Delete an item in an array

I have this code that browse all file types in VBA. It's already working but my what I want to do now is to delete the item in the array if it is one of the blocked file types.
Const exts = _
".ade.adp.app.asp.bas.bat.cer.chm.cmd.com.cpl.crt.csh.der.exe.fxp.gadget" & _
".hlp.hta.inf.ins.isp.its.js.jse.ksh.lnk.mad.maf.mag.mam.maq.mar.mas.mat" & _
".mau.mav.maw.mda.mdb.mde.mdt.mdw.mdz.msc.msh.msh1.msh2.mshxml.msh1xml" & _
".msh2xml.ade.adp.app.asp.bas.bat.cer.chm.cmd.com.cpl.crt.csh.der.exe.fxp" & _
".gadget.hlp.hta.msi.msp.mst.ops.pcd.pif.plg.prf.prg.pst.reg.scf.scr.sct" & _
".shb.shs.ps1.ps1xml.ps2.ps2xml.psc1.psc2.tmp.url.vb.vbe.vbs.vsmacros.vsw" & _
".ws.wsc.wsf.wsh.xnk."
file = Application.GetOpenFilename(MultiSelect:=True, Title:="Select the files you want to zip")
If IsArray(file) = True Then
'Create empty Zip File
ReDim Data(1 To UBound(file) + 1, 1 To 1)
efCount = Empty
' filter the list
For j = LBound(file) To UBound(file)
ext = LCase(Mid(file(j), InStrRev(file(j), ".")))
If InStr(1, exts, ext & ".") = 0 Then ' if not blacklisted
count = count + 1
Data(count, 1) = file(j)
Else
ReDim Preserve excludedFile(efCount)
excludedFile(efCount) = Dir(file(j))
efCount = efCount + 1
file(j - 1) = file(j) 'Ive tried this and other ways bu is not working
found = True
End If
Next
Thanks for the help.
you could go like this
Dim file As Variant
Dim efCount As Long, j As Long, count As Long
Dim ext As String
Dim found As Boolean
Const exts = _
".ade.adp.app.asp.bas.bat.cer.chm.cmd.com.cpl.crt.csh.der.exe.fxp.gadget" & _
".hlp.hta.inf.ins.isp.its.js.jse.ksh.lnk.mad.maf.mag.mam.maq.mar.mas.mat" & _
".mau.mav.maw.mda.mdb.mde.mdt.mdw.mdz.msc.msh.msh1.msh2.mshxml.msh1xml" & _
".msh2xml.ade.adp.app.asp.bas.bat.cer.chm.cmd.com.cpl.crt.csh.der.exe.fxp" & _
".gadget.hlp.hta.msi.msp.mst.ops.pcd.pif.plg.prf.prg.pst.reg.scf.scr.sct" & _
".shb.shs.ps1.ps1xml.ps2.ps2xml.psc1.psc2.tmp.url.vb.vbe.vbs.vsmacros.vsw" & _
".ws.wsc.wsf.wsh.xnk."
file = Application.GetOpenFilename(MultiSelect:=True, Title:="Select the files you want to zip")
If IsArray(file) = True Then
'Create empty Zip File
ReDim Data(1 To UBound(file))
ReDim excludedFile(1 To UBound(file))
efCount = 0
' filter the list
For j = LBound(file) To UBound(file)
ext = LCase(Mid(file(j), InStrRev(file(j), ".")))
If InStr(1, exts, ext & ".") = 0 Then ' if not blacklisted
count = count + 1
Data(count) = file(j)
Else
excludedFile(efCount + 1) = Dir(file(j))
efCount = efCount + 1
End If
Next
found = efCount > 0
End If
ReDim Preserve Data(1 To count)
ReDim Preserve excludedFile(1 To efCount)
file = Data
You can use function to delete particular value from array. Put this into your project:
Function DeleteElement(x As String, ByRef List() As String) ' As String
Dim i As Integer, el As Integer
Dim Result() As String
ReDim Result(UBound(List) - 1)
For i = 0 To UBound(List)
If x = List(i) Then
el = i
Exit For
End If
Next i
For i = 0 To UBound(Result)
If i < el Then
Result(i) = List(i)
Else
Result(i) = List(i + 1)
End If
Next i
DeleteElement = Result
End Function
You can use it like here:
Sub test2()
Dim arr1(3) As String
arr1(0) = "A"
arr1(1) = "B"
arr1(2) = "C"
arr1(3) = "D"
arr2 = DeleteElement("B", arr1)
End Sub

Why cannot be stored my Array?

I have this code in vba, trying to fill an dynamic array with data extracted from a text file but appears me an error
"subscripts out of range".
I did try to make this with non-zero based arrays but I receive the same error.
Module VBA
option explicit
Sub FromFileToExcel()
Dim Delimiter As String
Dim TextFile As Integer
Dim validRow As Integer
validRow = 0
Dim x As Integer
Dim i As Integer
Dim FilePath As String
Dim FileContent As String
Dim LineArray() As String
Dim DataArray() As String
FilePath = "C:\Users\Jlopez25\Desktop\bertha\INVPLANT.prn"
TextFile = FreeFile
Open FilePath For Input As TextFile
FileContent = Input(LOF(TextFile), TextFile)
Close TextFile
LineArray() = Split(FileContent, vbCrLf)
For x = LBound(LineArray) To UBound(LineArray)
If validateData(LineArray(x)) Then
ReDim Preserve DataArray(validRow, 3) 'here occours the mistake
DataArray(validRow, 1) = Left(LineArray(i), 8)
DataArray(validRow, 2) = Mid(LineArray(i), 9, 7)
DataArray(validRow, 3) = Mid(LineArray(i), 18, 2)
validRow = validRow + 1
End If
Next x
Range("a1").Resize(UBound(DataArray, 1), UBound(DataArray, 2)).Value = DataArray()
End Sub
UDF
Public Function validateData(Data As String) As Boolean
If InStr(1, Left(Data, 8), ":", vbTextCompare) = 0 And _
Len(Replace(Left(Data, 8), " ", "", , , vbTextCompare)) > 7 And _
Left(Data, 1) <> "_" Then
validateData = True
Else
validateData = False
End If
End Function
this are some lines of the text file that I want to separate into DataArray() :
abc:c
page: 1
____________________________
site Location item
MX823JXIA1B38C08 01
MX823JXIA9B06C58 02
MX823JXIA9B12C76 03
ReDim Preserve DataArray(validRow, 3) 'here occours the mistake
that is because you cannot Redim Preserve an Array by changing its first dimension, but only the last dimension. You might want to write your own custom function to achieve this special Redim.
But from your code, I can see that it was possible to calculate the size of the array in a first loop, then do the work in another loop. although it is slow (depends on the complexity of the validateData function), but it easy to achieve. Consider this:
Dim arSize as Integer
For x = LBound(LineArray) To UBound(LineArray)
If validateData(LineArray(x)) Then arsize = arSize + 1
Next
ReDim DataArray(arSize, 1 to 3) 'dimension the array
'And now do the calculation loop
For x = LBound(LineArray) To UBound(LineArray)
If validateData(LineArray(x)) Then
DataArray(validRow, 1) = Left(LineArray(i), 8)
DataArray(validRow, 2) = Mid(LineArray(i), 9, 7)
DataArray(validRow, 3) = Mid(LineArray(i), 18, 2)
validRow = validRow + 1
End If
If you size DataArray to match the size of the input file then you don't really need to keep resizing it. It likely doesn't matter that part of it remains empty...
Option Explicit
Sub FromFileToExcel()
Dim Delimiter As String
Dim validRow As Integer
validRow = 0
Dim x As Integer
Dim i As Integer
Dim FilePath As String
Dim LineArray() As String
Dim DataArray() As String
FilePath = "C:\Users\Jlopez25\Desktop\bertha\INVPLANT.prn"
LineArray() = Split(FileContent(FilePath), vbCrLf)
ReDim DataArray(1 To UBound(LineArray) + 1, 1 To 3)
For x = LBound(LineArray) To UBound(LineArray)
If validateData(LineArray(x)) Then
validRow = validRow + 1
DataArray(validRow, 1) = Left(LineArray(i), 8)
DataArray(validRow, 2) = Mid(LineArray(i), 9, 7)
DataArray(validRow, 3) = Mid(LineArray(i), 18, 2)
End If
Next x
Range("a1").Resize(UBound(DataArray, 1), UBound(DataArray, 2)).Value = DataArray()
End Sub
Public Function validateData(Data As String) As Boolean
If InStr(1, Left(Data, 8), ":", vbTextCompare) = 0 And _
Len(Replace(Left(Data, 8), " ", "", , , vbTextCompare)) > 7 And _
Left(Data, 1) <> "_" Then
validateData = True
Else
validateData = False
End If
End Function
Function FileContent(sPath As String) As String
Dim TextFile As Integer
TextFile = FreeFile
Open FilePath For Input As TextFile
FileContent = Input(LOF(TextFile), TextFile)
Close TextFile
End Function

Error when splitting a string into an array

Basically, I can't trim it unless it's in a message box.. it's hard to explain.
Here's 2 Images:
1. http://gyazo.com/83b0a996e607f7013d998f6f800650f1
2. http://gyazo.com/e1fe9d8adb4a522479f6621d29e90e9d
Dim value As String = ary(0).Trim()
'Dim value1 As String = ary(1).Trim()
Dim R As String
Dim G As String
Dim B As String
Dim outline As String
Dim outlineColor As String
R = Chr(34) & "MouseColorR" & Chr(34)
G = Chr(34) & "MouseColorG" & Chr(34)
B = Chr(34) & "MouseColorB" & Chr(34)
outline = Chr(34) & "ThickMouseEdges" & Chr(34)
outlineColor = Chr(34) & "ThickMouseEdgesPackedColor" & Chr(34)
'based on the value after the equals sign, do something
If value = R Then
MsgBox(ary(1).Trim(Chr(44)))
ElseIf value = G Then
MsgBox("finally")
ElseIf value = B Then
MsgBox("finally")
ElseIf value = outline Then
MsgBox("finally")
this works^^^^
this doesnt:
Dim value As String = ary(0).Trim()
this is the error---> Dim value1 As String = ary(1).Trim()
Dim R As String
Dim G As String
Dim B As String
Dim outline As String
Dim outlineColor As String
R = Chr(34) & "MouseColorR" & Chr(34)
G = Chr(34) & "MouseColorG" & Chr(34)
B = Chr(34) & "MouseColorB" & Chr(34)
outline = Chr(34) & "ThickMouseEdges" & Chr(34)
outlineColor = Chr(34) & "ThickMouseEdgesPackedColor" & Chr(34)
'based on the value after the equals sign, do something
If value = R Then
MsgBox(ary(1).Trim(Chr(44)))
ElseIf value = G Then
MsgBox("finally")
ElseIf value = B Then
MsgBox("finally")
ElseIf value = outline Then
MsgBox("finally")
ElseIf value = outlineColor Then
MsgBox("finally")
and the error is :An unhandled exception of type 'System.IndexOutOfRangeException' occurred in Terraria Smart Cursor.exe
Additional information: Index was outside the bounds of the array.
Whole Code:
Public Class Form1
Private Sub NsCheckBox1_CheckedChanged(sender As Object) Handles NsCheckBox1.CheckedChanged
NsGroupBox2.Enabled = NsCheckBox1.Checked
End Sub
Private Sub NsTrackBar1_Scroll(sender As Object) Handles NsTrackBar1.Scroll
NsLabel4.Value1 = NsTrackBar1.Value
End Sub
Private Sub NsTrackBar2_Scroll(sender As Object) Handles NsTrackBar2.Scroll
NsLabel5.Value1 = NsTrackBar2.Value
End Sub
Private Sub NsTrackBar3_Scroll(sender As Object) Handles NsTrackBar3.Scroll
NsLabel6.Value1 = NsTrackBar3.Value
End Sub
Private Sub NsTrackBar6_Scroll(sender As Object) Handles NsTrackBar6.Scroll
NsLabel9.Value1 = NsTrackBar6.Value
End Sub
Private Sub NsTrackBar5_Scroll(sender As Object) Handles NsTrackBar5.Scroll
NsLabel8.Value1 = NsTrackBar5.Value
End Sub
Private Sub NsTrackBar4_Scroll(sender As Object) Handles NsTrackBar4.Scroll
NsLabel7.Value1 = NsTrackBar4.Value
End Sub
Private Sub NsButton1_Click_1(sender As Object, e As EventArgs) Handles NsButton1.Click
If ColorDialog1.ShowDialog() = Windows.Forms.DialogResult.OK Then
NsLabel4.Value1 = ColorDialog1.Color.R
NsLabel5.Value1 = ColorDialog1.Color.G
NsLabel6.Value1 = ColorDialog1.Color.B
NsTrackBar1.Value = NsLabel4.Value1
NsTrackBar2.Value = NsLabel5.Value1
NsTrackBar3.Value = NsLabel6.Value1
End If
End Sub
Private Sub NsButton2_Click_1(sender As Object, e As EventArgs) Handles NsButton2.Click
If ColorDialog2.ShowDialog() = Windows.Forms.DialogResult.OK Then
NsLabel9.Value1 = ColorDialog2.Color.R
NsLabel8.Value1 = ColorDialog2.Color.G
NsLabel7.Value1 = ColorDialog2.Color.B
NsTrackBar6.Value = NsLabel9.Value1
NsTrackBar5.Value = NsLabel8.Value1
NsTrackBar4.Value = NsLabel7.Value1
End If
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'reads each line from the text file one at a time
For Each line As String In IO.File.ReadLines("C:\Users\Matthew\Documents\My Games\Terraria\config.json")
'split the string by equals sign
Dim ary As String() = line.Split(":")
Dim value As String = ary(0).Trim()
Dim value1 As String = ary(1).Trim()
Dim R As String
Dim G As String
Dim B As String
Dim outline As String
Dim outlineColor As String
R = Chr(34) & "MouseColorR" & Chr(34)
G = Chr(34) & "MouseColorG" & Chr(34)
B = Chr(34) & "MouseColorB" & Chr(34)
outline = Chr(34) & "ThickMouseEdges" & Chr(34)
outlineColor = Chr(34) & "ThickMouseEdgesPackedColor" & Chr(34)
'based on the value after the equals sign, do something
If value = R Then
MsgBox(ary(1).Trim(Chr(44)))
ElseIf value = G Then
MsgBox("finally")
ElseIf value = B Then
MsgBox("finally")
ElseIf value = outline Then
MsgBox("finally")
ElseIf value = outlineColor Then
MsgBox("finally")
End If
Next
End Sub
End Class
You are populating ary via Dim ary As String() = line.Split(":"). You are reading each line from C:\Users\Matthew\Documents\My Games\Terraria\config.json
One of the lines of that file doesn't contain a :. So the Split creates an array of just one element. When you then call:
Dim value1 As String = ary(1).Trim()
you get an ArgumentOutOfRangeException as element 1 doesn't exist.
The solution is to test the array length and have you code handle a line without a : in a graceful fashion.
You're accessing a container at position 0 which works and then at 1 which does not. The exception displayed in the screenshot says it's an out-of-range problem. So your container isn't as large as you expect: It has only 1 element.

Dynamically dimension a two-dimensional array in VBA [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I'm modelling Petri nets using VBA in Excel, and I want to be able to vary the number of species and transitions, and the links between them. I'm hoping to do this by reading straight off the Shapes used to draw the network, rather than explicitly inputting the matrices. This means I have to dynamically dimension my array variables. I can do this for the one-dimensional arrays, but the Species-Transition links require two-dimensional arrays. Is there any way of doing this, or will I have to fall back on using the spreadsheet to store my variables in?
As requested, here is the clsMatrix class I had put together for my purposes; hopefully it can serve yours as well.
It includes:
Matrix operations - Add, Subtract, Multiply, ScalarMultiply, Augment, Transpose
Elementary Row Operations - SwapRows, ScaleRow, AddScalarMultipleRow
A Parser for loading the Matrix from a String - LoadMatrixString
Utility functions - toString, Clone
An implementation of Gaussian Elimination - RowReduce
Here are a couple examples of usage:
Public Sub TestMatrix()
Dim m1 As clsMatrix
Set m1 = New clsMatrix
m1.LoadMatrixString ("[[1,-3,1]," & _
" [1,1,-1]," & _
" [3,11,5]]")
Dim m2 As clsMatrix
Set m2 = New clsMatrix
m2.LoadMatrixString ("[[9]," & _
" [1]," & _
" [35]]")
MsgBox m1.Augment(m2).RowReduce.toString
End Sub
Public Sub TestMatrix2()
'This is an example iteration of a matrix Petri Net as described here:
'http://www.techfak.uni-bielefeld.de/~mchen/BioPNML/Intro/MRPN.html
Dim D_Minus As clsMatrix
Dim D_Plus As clsMatrix
Dim D As clsMatrix
Set D_Minus = New clsMatrix
D_Minus.LoadMatrixString "[[0, 0, 0, 0, 1]," & _
" [1, 0, 0, 0, 0]," & _
" [0, 1, 0, 0, 0]," & _
" [0, 0, 1, 1, 0]]"
Set D_Plus = New clsMatrix
D_Plus.LoadMatrixString "[[1, 1, 0, 0, 0]," & _
" [0, 0, 1, 1, 0]," & _
" [0, 0, 0, 1, 0]," & _
" [0, 0, 0, 0, 1]]"
Set D = D_Plus.Subtract(D_Minus)
MsgBox D.toString
Dim Transition_Matrix As clsMatrix
Dim Marking_Matrix As clsMatrix
Dim Next_Marking As clsMatrix
Set Transition_Matrix = New clsMatrix
Transition_Matrix.LoadMatrixString "[[0, 1, 1, 0]]"
Set Marking_Matrix = New clsMatrix
Marking_Matrix.LoadMatrixString "[[2, 1, 0, 0, 0]]"
Set Next_Marking = Transition_Matrix.Multiply(D).Add(Marking_Matrix)
MsgBox Next_Marking.toString
End Sub
And here is the clsMatrix class:
Option Compare Database
Option Explicit
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" (ByVal Destination As Long, ByVal Source As Long, ByVal Length As Integer)
Private m_Arr() As Double
Private m_strMatrix As String
Private Look As String
Private Type SAFEARRAYBOUND
cElements As Long
lLbound As Long
End Type
Private Type SAFEARRAY
cDims As Integer
fFeatures As Integer
cbElements As Long
cLocks As Long
pvData As Long
End Type
Private Type ARRAY_VARIANT
vt As Integer
wReserved1 As Integer
wReserved2 As Integer
wReserved3 As Integer
lpSAFEARRAY As Long
data(4) As Byte
End Type
Private Enum tagVARENUM
VT_EMPTY = &H0
VT_NULL
VT_I2
VT_I4
VT_R4
VT_R8
VT_CY
VT_DATE
VT_BSTR
VT_DISPATCH
VT_ERROR
VT_BOOL
VT_VARIANT
VT_UNKNOWN
VT_DECIMAL
VT_I1 = &H10
VT_UI1
VT_UI2
VT_I8
VT_UI8
VT_INT
VT_VOID
VT_HRESULT
VT_PTR
VT_SAFEARRAY
VT_CARRAY
VT_USERDEFINED
VT_LPSTR
VT_LPWSTR
VT_RECORD = &H24
VT_INT_PTR
VT_UINT_PTR
VT_ARRAY = &H2000
VT_BYREF = &H4000
End Enum
Public Sub Class_Initialize()
End Sub
'************************************************
'* Accessors and Utility Functions *
'***********************************
Public Property Get Value(r As Long, c As Long) As Double
CheckDimensions
Value = m_Arr(r, c)
End Property
Public Property Let Value(r As Long, c As Long, val As Double)
CheckDimensions
m_Arr(r, c) = val
End Property
Public Property Get Rows() As Long
If GetDims(m_Arr) = 0 Then
Rows = 0
Else
Rows = UBound(m_Arr, 1) + 1
End If
End Property
Public Property Get Cols() As Long
If GetDims(m_Arr) = 0 Then
Cols = 0
Else
Cols = UBound(m_Arr, 2) + 1
End If
End Property
Public Sub LoadMatrixString(str As String)
m_strMatrix = str
ParseMatrix str
m_strMatrix = ""
Look = ""
End Sub
Public Sub Resize(Rows As Long, Cols As Long, Optional blPreserve As Boolean = False)
Dim tempMatrix As clsMatrix
Dim r As Long
Dim c As Long
If blPreserve Then
CheckDimensions
Set tempMatrix = Me.Clone
ReDim m_Arr(0 To Rows - 1, 0 To Cols - 1)
For r = 0 To MinLongs(tempMatrix.Rows, Me.Rows) - 1
For c = 0 To MinLongs(tempMatrix.Cols, Me.Cols) - 1
Value(r, c) = tempMatrix.Value(r, c)
Next
Next
Else
ReDim m_Arr(0 To Rows - 1, 0 To Cols - 1)
End If
End Sub
Public Function Clone() As clsMatrix
Dim mresult As clsMatrix
Dim r As Long
Dim c As Long
CheckDimensions
Set mresult = New clsMatrix
mresult.Resize Me.Rows, Me.Cols
For r = 0 To Me.Rows - 1
For c = 0 To Me.Cols - 1
mresult.Value(r, c) = Me.Value(r, c)
Next
Next
Set Clone = mresult
End Function
Public Function toString() As String
Dim str As String
Dim r As Long
Dim c As Long
Dim tempRow() As String
Dim tempRows() As String
ReDim tempRow(0 To Me.Cols - 1)
ReDim tempRows(0 To Me.Rows - 1)
If Not GetDims(m_Arr) = 0 Then 'Need to check if array is empty
For r = 0 To Me.Rows - 1
For c = 0 To Me.Cols - 1
tempRow(c) = Me.Value(r, c)
Next
tempRows(r) = "[" & Join(tempRow, ", ") & "]"
Next
toString = "[" & Join(tempRows, vbCrLf) & "]"
Else
toString = ""
End If
End Function
'***********************************************************
'* Matrix Operations *
'*********************
Public Function Add(m As clsMatrix) As clsMatrix
Dim mresult As clsMatrix
Dim r As Long
Dim c As Long
CheckDimensions
If m.Rows = Me.Rows And m.Cols = Me.Cols Then
Set mresult = New clsMatrix
mresult.Resize Me.Rows, Me.Cols
For r = 0 To Me.Rows - 1
For c = 0 To Me.Cols - 1
mresult.Value(r, c) = Me.Value(r, c) + m.Value(r, c)
Next
Next
Else
Err.Raise vbObjectError + 1, "clsMatrix.Add", "Could not Add matrices: the Rows and Columns must be the same. The left matrix is (" & Me.Rows & ", " & Me.Cols & ") and the right matrix is (" & m.Rows & ", " & m.Cols & ")."
End If
Set Add = mresult
End Function
Public Function Subtract(m As clsMatrix) As clsMatrix
Dim mresult As clsMatrix
Dim r As Long
Dim c As Long
CheckDimensions
If m.Rows = Me.Rows And m.Cols = Me.Cols Then
Set mresult = New clsMatrix
mresult.Resize Me.Rows, Me.Cols
For r = 0 To Me.Rows - 1
For c = 0 To Me.Cols - 1
mresult.Value(r, c) = Me.Value(r, c) - m.Value(r, c)
Next
Next
Else
Err.Raise vbObjectError + 2, "clsMatrix.Subtract", "Could not Subtract matrices: the Rows and Columns must be the same. The left matrix is (" & Me.Rows & ", " & Me.Cols & ") and the right matrix is (" & m.Rows & ", " & m.Cols & ")."
End If
Set Subtract = mresult
End Function
Public Function Multiply(m As clsMatrix) As clsMatrix
Dim mresult As clsMatrix
Dim i As Long
Dim j As Long
Dim n As Long
CheckDimensions
If Me.Cols = m.Rows Then
Set mresult = New clsMatrix
mresult.Resize Me.Rows, m.Cols
For i = 0 To Me.Rows - 1
For j = 0 To m.Cols - 1
For n = 0 To Me.Cols - 1
mresult.Value(i, j) = mresult.Value(i, j) + (Me.Value(i, n) * m.Value(n, j))
Next
Next
Next
Else
Err.Raise vbObjectError + 3, "clsMatrix.Multiply", "Could not Subtract matrices: the Columns of the left matrix and Rows of the right must be the same. The left matrix has " & Me.Cols & " Columns and the right matrix has " & m.Rows & " Rows."
End If
Set Multiply = mresult
End Function
Public Function ScalarMultiply(scalar As Double) As clsMatrix
Dim mresult As clsMatrix
Dim r As Long
Dim c As Long
CheckDimensions
Set mresult = New clsMatrix
mresult.Resize Me.Rows, Me.Cols
For r = 0 To Me.Rows - 1
For c = 0 To Me.Cols - 1
mresult.Value(r, c) = Me.Value(r, c) * scalar
Next
Next
Set ScalarMultiply = mresult
End Function
Public Function Augment(m As clsMatrix) As clsMatrix
Dim mresult As clsMatrix
Dim r As Long
Dim c As Long
CheckDimensions
If Me.Rows = m.Rows Then
Set mresult = New clsMatrix
mresult.Resize Me.Rows, Me.Cols + m.Cols
For r = 0 To Me.Rows - 1
For c = 0 To Me.Cols - 1
mresult.Value(r, c) = Me.Value(r, c)
Next
Next
For r = 0 To Me.Rows - 1
For c = 0 To m.Cols - 1
mresult.Value(r, Me.Cols + c) = m.Value(r, c)
Next
Next
Else
Err.Raise vbObjectError + 4, "clsMatrix.Augment", "Could not Augment matrices: the matrices must have the same number of Rows. The left matrix has " & Me.Rows & " Rows and the right matrix has " & m.Rows & " Rows."
End If
Set Augment = mresult
End Function
Public Function Transpose() As clsMatrix
Dim mresult As clsMatrix
Dim r As Long
Dim c As Long
CheckDimensions
If Me.Rows = Me.Cols Then
Set mresult = New clsMatrix
mresult.Resize Me.Cols, Me.Rows
For r = 0 To Me.Rows - 1
For c = 0 To Me.Cols - 1
Me.Value(r, c) = mresult(c, r)
Next
Next
Else
Err.Raise vbObjectError + 5, "clsMatrix.Augment", "Could not Transpose matrix: the matrix must have the same number of Rows and Cols. The matrix is (" & Me.Rows & ", " & Me.Cols & ")."
End If
Set Transpose = mresult
End Function
Public Function RowReduce() As clsMatrix
Dim i As Long
Dim j As Long
CheckDimensions
'Row Echelon
Dim mresult As clsMatrix
Set mresult = Me.Clone
For i = 0 To mresult.Rows - 1
If Not mresult.Value(i, i) <> 0 Then
For j = i + 1 To mresult.Rows - 1
If mresult.Value(j, i) > 0 Then
mresult.SwapRows i, j
Exit For
End If
Next
End If
If mresult.Value(i, i) = 0 Then
Exit For
End If
mresult.ScaleRow i, 1 / mresult.Value(i, i)
For j = i + 1 To mresult.Rows - 1
mresult.AddScalarMultipleRow i, j, -mresult.Value(j, i)
Next
Next
'Backwards substitution
For i = IIf(mresult.Rows < mresult.Cols, mresult.Rows, mresult.Cols) - 1 To 1 Step -1
If mresult.Value(i, i) > 0 Then
For j = i - 1 To 0 Step -1
mresult.AddScalarMultipleRow i, j, -mresult.Value(j, i)
Next
End If
Next
Set RowReduce = mresult
End Function
'*************************************************************
'* Elementary Row Operaions *
'****************************
Public Sub SwapRows(r1 As Long, r2 As Long)
Dim temp As Double
Dim c As Long
CheckDimensions
For c = 0 To Me.Cols - 1
temp = Me.Value(r1, c)
Me.Value(r1, c) = Me.Value(r2, c)
Me.Value(r2, c) = temp
Next
End Sub
Public Sub ScaleRow(row As Long, scalar As Double)
Dim c As Long
CheckDimensions
For c = 0 To Me.Cols - 1
Me.Value(row, c) = Me.Value(row, c) * scalar
Next
End Sub
Public Sub AddScalarMultipleRow(srcrow As Long, destrow As Long, scalar As Double)
Dim c As Long
CheckDimensions
For c = 0 To Me.Cols - 1
Me.Value(destrow, c) = Me.Value(destrow, c) + (Me.Value(srcrow, c) * scalar)
Next
End Sub
'************************************************************
'* Parsing Functions *
'*********************
Private Sub ParseMatrix(strMatrix As String)
Dim arr() As Double
Dim c As Long
GetChar 1
Match "["
SkipWhite
If Look = "[" Then
arr = ParseRow
Me.Resize 1, UBound(arr) + 1
'ReDim m_Arr(0 To UBound(arr), 0 To 0)
For c = 0 To Me.Cols - 1
Me.Value(0, c) = arr(c)
Next
SkipWhite
While Look = ","
Match ","
SkipWhite
arr = ParseRow
Me.Resize Me.Rows + 1, Me.Cols, True
If UBound(arr) <> (Me.Cols - 1) Then
'Error jagged array
Err.Raise vbObjectError + 6, "clsMatrix.LoadMatrixString", "Parser Error - Jagged arrays are not supported: Row 0 has " & Me.Cols & " Cols, but Row " & Me.Rows - 1 & " has " & UBound(arr) + 1 & " Cols."
End If
For c = 0 To Me.Cols - 1
Me.Value(Me.Rows - 1, c) = arr(c)
Next
SkipWhite
Wend
Match "]"
ElseIf Look = "]" Then
Match "]"
Else
MsgBox "Error"
End If
SkipWhite
If Look <> "" Then
Err.Raise vbObjectError + 7, "clsMatrix.LoadMatrixString", "Parser Error - Unexpected Character: """ & Look & """."
End If
End Sub
Private Function ParseRow() As Variant
Dim arr() As Double
Match "["
SkipWhite
ReDim arr(0 To 0)
arr(0) = ParseNumber
SkipWhite
While Look = ","
Match ","
ReDim Preserve arr(0 To UBound(arr) + 1)
arr(UBound(arr)) = ParseNumber
SkipWhite
Wend
Match "]"
ParseRow = arr
End Function
Private Function ParseNumber() As Double
Dim strToken As String
If Look = "-" Then
strToken = strToken & Look
GetChar
End If
While IsDigit(Look)
strToken = strToken & Look
GetChar
Wend
If Look = "." Then
strToken = strToken & Look
GetChar
While IsDigit(Look)
strToken = strToken & Look
GetChar
Wend
End If
ParseNumber = CDbl(strToken)
End Function
'****************************************************************
Private Sub GetChar(Optional InitValue)
Static i As Long
If Not IsMissing(InitValue) Then
i = InitValue
End If
If i <= Len(m_strMatrix) Then
Look = Mid(m_strMatrix, i, 1)
i = i + 1
Else
Look = ""
End If
End Sub
'****************************************************************
'* Skip Functions *
'******************
Private Sub SkipWhite()
While IsWhite(Look) Or IsEOL(Look)
GetChar
Wend
End Sub
'****************************************************************
'* Match/Expect Functions *
'**************************
Private Sub Match(char As String)
If Look <> char Then
Expected """" & char & """"
Else
GetChar
SkipWhite
End If
Exit Sub
End Sub
Private Sub Expected(str As String)
'MsgBox "Expected: " & str
Err.Raise vbObjectError + 8, "clsMatrix.LoadMatrixString", "Parser Error - Expected: " & str
End Sub
'****************************************************************
'* Character Class Functions *
'*****************************
Private Function IsDigit(char As String) As Boolean
Dim charval As Integer
If char <> "" Then
charval = Asc(char)
If 48 <= charval And charval <= 57 Then
IsDigit = True
Else
IsDigit = False
End If
Else
IsDigit = False
End If
End Function
Private Function IsWhite(char As String) As Boolean
Dim charval As Integer
If char <> "" Then
charval = Asc(char)
If charval = 9 Or charval = 11 Or charval = 12 Or charval = 32 Or charval = 160 Then '160 because MS Exchange sucks
IsWhite = True
Else
IsWhite = False
End If
Else
IsWhite = False
End If
End Function
Private Function IsEOL(char As String) As Boolean
If char = Chr(13) Or char = Chr(10) Then
IsEOL = True
Else
IsEOL = False
End If
End Function
'*****************************************************************
'* Helper Functions *
'********************
Private Sub CheckDimensions()
If GetDims(m_Arr) = 0 Then
'Error, uninitialized array
Err.Raise vbObjectError + 1, "clsMatrix", "Array has not been initialized"
End If
End Sub
Private Function GetDims(VarSafeArray As Variant) As Integer
Dim varArray As ARRAY_VARIANT
Dim lpSAFEARRAY As Long
Dim sArr As SAFEARRAY
CopyMemory VarPtr(varArray.vt), VarPtr(VarSafeArray), 16&
If varArray.vt And (tagVARENUM.VT_ARRAY Or tagVARENUM.VT_BYREF) Then
CopyMemory VarPtr(lpSAFEARRAY), varArray.lpSAFEARRAY, 4&
If Not lpSAFEARRAY = 0 Then
CopyMemory VarPtr(sArr), lpSAFEARRAY, LenB(sArr)
GetDims = sArr.cDims
Else
GetDims = 0 'The array is uninitialized
End If
Else
GetDims = 0 'Not an array
End If
End Function
Private Function MinLongs(a As Long, b As Long) As Long
If a < b Then
MinLongs = a
Else
MinLongs = b
End If
End Function
If you should decide to try it and if you should encounter any problems/issues/unhandled exceptions, it would be very helpful to me if you could make note of them in a comment below.
Suppose your worksheet looks like this:
You could dynamically allocate a MyArray variable like this:
Option Explicit
Sub DynamicDimension()
Dim NumRows As Long, NumCols As Long
Dim MyArray As Variant
'collect the number of rows from cell A1
'and the number of columns from cell B1
NumRows = Worksheets("Sheet1").Range("A1").Value
NumCols = Worksheets("Sheet1").Range("B1").Value
'allocate array with dimensions collected from A1 and B1
ReDim MyArray(1 To NumRows, 1 To NumCols)
'output with message box to show that array is correctly dimensioned
MsgBox ("MyArray has " & UBound(MyArray, 1) & " rows.")
MsgBox ("MyArray has " & UBound(MyArray, 2) & " cols.")
End Sub

Resources