Excelrecursion过程来创build一个数组

我有以下数据:

数据

我的目标是做到这一点:

  • recursion子将创build一个填充材料的数组。
  • 每次将材质设置为“精雕细琢”时,该arrays将在同一维度上添加另一个子材质,并添加“.1”。 例如:如果我们看弓,它是精心devise的,所以当完成时数组看起来像这样:Material:array(0,0,0)= Wood,Quantity:array(0,0,1)= 2,Level:array (0,0,2)= 1。
  • 但是,子代会变成:材料:array(0,1,0)=分支,数量:array(0,1,1)= 2,Level:array(0,1,2)= 1.1
  • 并且由于分支是制作的:材料:数组(0,2,0)=树,数量:数组(0,2,1)= 1,级别:数组(0,2,2)= 1.1.1。
  • 然后:材料:数组(0,3,0)=叶,数量:数组(0,3,1)= 9,级别:数组(0,3,2)= 1.2。
  • 然后查找下一个材质“Rope”并继续:Material:array(1,0,0)= Rope,Quantity:array(1,0,1)= 1,Level:array(1,0,2 )= 1,材料:array(1,1,0)= Web,Quantity:array(1,1,1)= 10,Level:array(1,1,2)= 2.1等等。

我的主要问题是我不熟悉recursion代码,我的逻辑似乎是错误的,所以我想我会寻求帮助,并问在这里如何可以做到。

这是我的代码到目前为止,这是部分工作:

Sub Look(ByRef arrayMaterials) Dim item With ActiveSheet lastRow = .Cells(.Rows.Count, "A").End(xlUp).Row End With With ActiveSheet lastColumn = .Cells(j + 2, .Columns.Count).End(xlToLeft).Column End With For i = 0 To lastRow For y = 0 To lastColumn item = Cells(i + 2, 1).Value If Cells(i + 1, y + 1).Value = item And Cells(i + 1, y + 1).Value <> "Item" Then arrayMaterials = ReDimPreserve(arrayMaterials, i, i, y) arrayMaterials(i - 1, i - 1, y - 2) = Cells(i + 1, y + 1).Value arrayMaterials(i - 1, i - 1, y - 1) = Cells(i + 1, y + 2).Value level = level & CInt(Right(Cells(1, y + 3), 2)) arrayMaterials(i - 1, i - 1, y) = level level = CInt(Right(Cells(1, y + 3), 2)) If Cells(i + 1, y + 1).Value <> "Resource" Then level = level & "." Look (arrayMaterials) End If End If Next Next Look (arrayMaterials) End Sub 

调用人:

 Sub CallLook() Dim arrayMaterials(1, 1, 1) Look (arrayMaterials) End Sub 

另外(摆脱最后维度的保留限制):

 Public Function ReDimPreserve(aArrayToPreserve, nNewFirstUBound, nNewSecondUBound, nNewLastUBound) ReDimPreserve = False 'check if its in array first If IsArray(aArrayToPreserve) Then 'create new array ReDim aPreservedArray(nNewFirstUBound, nNewSecondUBound, nNewLastUBound) 'get old lBound/uBound nOldFirstUBound = UBound(aArrayToPreserve, 1) nOldSecondUBound = UBound(aArrayToPreserve, 2) nOldLastUBound = UBound(aArrayToPreserve, 3) 'loop through first For nFirst = LBound(aArrayToPreserve, 1) To nNewFirstUBound For nSecond = LBound(aArrayToPreserve, 2) To nNewSecondUBound For nLast = LBound(aArrayToPreserve, 3) To nNewLastUBound 'if its in range, then append to new array the same way If nOldFirstUBound >= nFirst And nOldSecondUBound >= nSecond And nOldLastUBound >= nLast Then aPreservedArray(nFirst, nSecond, nLast) = aArrayToPreserve(nFirst, nSecond, nLast) End If Next Next Next 'return the array redimmed If IsArray(aPreservedArray) Then ReDimPreserve = aPreservedArray End If End Function 

variables“level”是全局声明的。

你能帮我解释一下这个代码不能使用吗?

我想我可能会有一些指标(我和y)是错的。 我也没有编码经验。

所有的帮助表示赞赏。

编辑:如在评论中所要求的,这里是arry和Excel的输出:

阵:

 (0,0,0) = Wood, 2, 1 (0,1,0) = Branch, 2, 1.1 (0,1,1) = Tree, 1, 1.1.1 (0,2,0) = Leaf, 9, 1.2 (1,0,0) = Rope, 1, 2 (1,1,0) = Web, 10, 2.1 (1,1,1) = Spider, 5, 2.1.1 (2,0,0) = Crystal, 3, 3 (3,0,0) = Shard, 8, 4 (4,0,0) = Plumes, 1, 5 (4,1,0) = Bird, 1, 5.1 

Excel(每个条目是一行,项目和数量在同一列,由于限制,我不能添加一列):

 Bow (is already on the other sheet, no need to add it, "-" are indents) -Wood - 2 --Branch - 4 (2 Wood, so 4 Branches) ---Tree - 4 --Leaf - 18 -Rope - 1 --Web - 10 ---Spider - 50 -Crystal - 3 -Shard - 8 -Plumes - 1 --Birds - 1 

我希望这能让你更清楚我需要什么。

编辑:2015-07-13 – 根据Tony Dallimore的build议添加了新的代码:

请注意,这不是成品,我仍然需要通过我想要的材料和编码输出的项目,我想确保我会理解一切,然后再进一步。

在我的数据表中,我有一个调用sFilltypes的button。

 Public Type tComponent RowMaterial As Long Quantity As Long End Type Public Type tMaterial Name As String Crafted As Boolean Used As Boolean Component() As tComponent End Type Sub sFillTypes() Dim count Dim Materials() As tMaterial With ActiveSheet lastRow = .Cells(.Rows.count, "A").End(xlUp).Row End With ReDim Materials(1 To lastRow - 1) For i = 2 To lastRow count = 0 With ActiveSheet lastColumn = .Cells(i, .Columns.count).End(xlToLeft).Column For k = 1 To lastColumn If Left(Cells(1, k), 8) = "Material" And Cells(1, k).Value <> "" Then count = count + 1 End If Next End With ReDim Materials(i - 1).Component(1 To 1) If UBound(Materials(i - 1).Component, 1) <= count Then ReDim Materials(i - 1).Component(1 To count) Else Erase Materials(i - 1).Component End If Materials(i - 1).Name = Cells(i, 1).Value If Cells(i, 2).Value = "Crafted" Then Materials(i - 1).Crafted = "True" Else Materials(i - 1).Crafted = "False" End If For y = 1 To lastColumn + 1 If InStr(Cells(1, y).Value, "Material") Then For Z = 1 To lastRow If Cells(i, y).Value = Cells(Z, 1).Value Then Materials(i - 1).Component(Right(Cells(1, y), 2)).RowMaterial = Z Materials(i - 1).Component(Right(Cells(1, y), 2)).Quantity = Cells(i, y + 1) End If Next End If Next Next End Sub 

每个答案都有30,000个字符,我必须接近。 将第一个答案与答案中的后续分开也是有价值的。

你的日常工作有些问题需要纠正。 我已经做了一些OTT,并且为了好的做法而做了修改。 我还添加了一个例程,显示材料作为检查它是正确的。 研究我的代码,并试图确定为什么我已经做了我有的变化。 根据需要回来提问。

 Option Explicit Public Type tComponent RowMaterial As Long Quantity As Long End Type Public Type tMaterial Name As String Crafted As Boolean Used As Boolean Component() As tComponent End Type Sub sFillTypes() ' Constants have two major benefits: ' * Instead of literals your code contains meaningful names making your ' code easier to read. ' * If the value changes, one amendment here fixes the code. For example, ' suppose a new column is added on the left. Looking through the code ' deciding which 2s, 3s and 4s are to be changed to 3s, 4s and 5s is ' nightmare. ' Variable names should be meaningful. Reading code full of Ks, Xs and Ys ' is difficult because the reader has to remember what they are. My system ' is to use a sequence of words or abbreviations. The first word says what ' I am using the variable for: Col=column number, Row=row number, etc. ' Each additional word reduces the scope until I have a unique name. I do ' not know the name of your worksheet so I have used Sht as the second word ' of variables that relate to the worksheet. Crnt (=current), First and Last ' are common words in my names. I can look at routines I wrote 10 years ago ' and immediately know what all the variables are which is a real help when ' trying to update them. If you do not like my system, develop your own. Const ColShtItem As Long = 1 Const ColShtType As Long = 2 Const ColShtMatFirst As Long = 3 Const RowShtDataFirst As Long = 2 Dim ColShtCrnt As Long Dim ColShtLast As Long Dim ColShtMatLast As Long Dim ColShtUsed As Long Dim Found As Boolean Dim InxComp As Long Dim Materials() As tMaterial Dim RowShtCrnt As Long Dim RowShtItem As Long Dim RowShtLast As Long Dim ValuesSht As Variant With ActiveSheet ' Cell.End is a convenient way of finding the last used cell in a row or column. ' It is probably a reliable way of finding the last row of your worksheet but you ' are relying on row 1 having a complete set of headers to determine the last column ' which makes me uncomfortable. RowShtLast = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByRows, xlPrevious).Row ColShtLast = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByColumns, xlPrevious).Column ' I do not know what you are doing with Count but this code cannot be at the top. Each ' row will have its own number of materials ' * This statements loads the values of the range to ValuesSht as an array. ' * I have loaded the first data row to the last row because I do not want the ' header row. I have loaded column 1 to last column plus 1 because I want an extra, ' blank column on the left. ' * ValuesSht will become a 2D array with the first dimension being for rows and the ' second for columns. ' * The top left cell of ValuesSht will always be (1,1) even if the range does not ' start in cell (1,1). ValuesSht = .Range(Cells(RowShtDataFirst, 1), .Cells(RowShtLast, ColShtLast + 1)) End With ReDim Materials(1 To UBound(ValuesSht, 1)) ' I will use the RowSht variables for ValuesSht even though the worksheet and array ' rows do not match because I have finished with the worksheet. The worksheet and ' array columns match so I will use the ColSht variables for both. ' I will also use the RowSht variables for Materials since the rows match. ColShtUsed = ColShtLast + 1 ' I load an extra column to hold used values For RowShtCrnt = 1 To UBound(ValuesSht, 1) ' Copy across the non-repeating values Materials(RowShtCrnt).Name = Trim(ValuesSht(RowShtCrnt, ColShtItem)) Select Case LCase(Trim(ValuesSht(RowShtCrnt, ColShtType))) Case "crafted" Materials(RowShtCrnt).Crafted = True Case "resource" Materials(RowShtCrnt).Crafted = False Case Else ' Do not assume the worksheet is perfect. Call MsgBox("Cell B" & RowShtCrnt + RowShtDataFirst - 1 & _ " does nor contain ""Crafted"" or ""Resource""", vbOKOnly) Exit Sub End Select ' If materials are not always below the item that uses them, this block ' will have to be in its own loop after the rest of Materials has been created If ValuesSht(RowShtCrnt, ColShtUsed) = "U" Then Materials(RowShtCrnt).Used = True Else Materials(RowShtCrnt).Used = False End If If Materials(RowShtCrnt).Crafted Then ' Replace material names in columns ColShtMatFirst, ColShtMatFirst+2 and so on ' with the number of the row for the material. ' Loop over all possible material columns For ColShtCrnt = ColShtMatFirst To ColShtLast - 1 Step 2 If Trim(ValuesSht(RowShtCrnt, ColShtCrnt)) = "" Then ColShtMatLast = ColShtCrnt - 2 Exit For End If ' Look down the remainder of ValuesSht for this material. ' This relies on used materials always being below the material they are ' used to make. This is a easy way of (1) preventing loops and (2) ensuring ' the used column is ready when required. If materials are not in this ' sequence, you will need a more sophisticated method of detecting loops such ' as: Material1 used to make Material2, Material2 used to make Material3 and ' Material3 used to make Material1. Found = False For RowShtItem = RowShtCrnt + 1 To UBound(ValuesSht, 1) If Trim(ValuesSht(RowShtItem, ColShtItem)) = _ Trim(ValuesSht(RowShtCrnt, ColShtCrnt)) Then ValuesSht(RowShtCrnt, ColShtCrnt) = RowShtItem Found = True Exit For End If Next RowShtItem If Not Found Then Call MsgBox("I cannot find the material in cell " & _ ColNumToCode(ColShtCrnt) & RowShtCrnt + RowShtDataFirst - 1 & _ " (" & ValuesSht(RowShtCrnt, ColShtCrnt) & ") defined on rows " & _ RowShtCrnt + 2 & " to " & UBound(ValuesSht, 1) + 1, vbOKOnly) Exit Sub End If ValuesSht(RowShtItem, ColShtUsed) = "U" ' Record this item used Next ColShtCrnt ' For the current row, the material names in columns ColShtMatFirst, ColShtMatFirst+2 ' and so on have been replaced by row numbers. ColShtMatLast has been set as ' appropriate for this row. ' Size Components as required for this material and move component detals for ValuesSht ReDim Materials(RowShtCrnt).Component(1 To (ColShtMatLast - ColShtMatFirst) / 2 + 1) InxComp = 1 For ColShtCrnt = ColShtMatFirst To ColShtMatLast Step 2 Materials(RowShtCrnt).Component(InxComp).RowMaterial = ValuesSht(RowShtCrnt, ColShtCrnt) Materials(RowShtCrnt).Component(InxComp).Quantity = ValuesSht(RowShtCrnt, ColShtCrnt + 1) InxComp = InxComp + 1 Next End If ' Materials(RowShtCrnt).Crafted Next RowShtCrnt ' Delete or comment out this line when you are satified the above code is correct. Call ListMaterials(Materials) End Sub Sub ListMaterials(ByRef Materials() As tMaterial) ' Debug.Print is very useful when debugging code. The only downside is that the ' Immediate Window will only hold 200 or so lines. After that, line at the top ' get lost. If I have or expect too many lines for the Immediate Window, I use ' a text file. Dim InxComp As Long Dim InxMat As Long Dim InxMatUsed As Long Dim LenMatNameMax As Long Dim Name As String Dim NumCompMax As Long ' Determine maximum length of a material name and the maximum number of ' components so the output can be formatted nicely. LenMatNameMax = 0 NumCompMax = 0 For InxMat = LBound(Materials) To UBound(Materials) If LenMatNameMax < Len(Materials(InxMat).Name) Then LenMatNameMax = Len(Materials(InxMat).Name) End If If Materials(InxMat).Crafted Then If NumCompMax < UBound(Materials(InxMat).Component) Then NumCompMax = UBound(Materials(InxMat).Component) End If End If Next InxMat ' List Materials and their components ' Output header line Debug.Print Left("Name" & Space(LenMatNameMax), LenMatNameMax) & " TU |"; For InxComp = 1 To NumCompMax Debug.Print Left("Material" & Space(LenMatNameMax), LenMatNameMax) & " Qty|"; Next Debug.Print ' Output materials For InxMat = LBound(Materials) To UBound(Materials) Debug.Print Left(Materials(InxMat).Name & Space(LenMatNameMax), LenMatNameMax + 1) & _ IIf(Materials(InxMat).Crafted, "C ", "R ") & _ IIf(Materials(InxMat).Used, "Y ", " ") & "|"; If Materials(InxMat).Crafted Then For InxComp = 1 To UBound(Materials(InxMat).Component) Name = Materials(Materials(InxMat).Component(InxComp).RowMaterial).Name Debug.Print Left(Name & Space(LenMatNameMax), LenMatNameMax + 1) & _ Right(" " & Materials(InxMat).Component(InxComp).Quantity, 3) & "|"; Next End If Debug.Print Next InxMat End Sub Function ColNumToCode(ByVal ColNum As Long) As String ' Last updated 3 Feb 12. Adapted to handle three character codes. Dim ColCode As String Dim PartNum As Long If ColNum = 0 Then ColNumToCode = "0" Else ColCode = "" Do While ColNum > 0 PartNum = (ColNum - 1) Mod 26 ColCode = Chr(65 + PartNum) & ColCode ColNum = (ColNum - PartNum - 1) \ 26 Loop End If ColNumToCode = ColCode End Function 

弓(已经在另一张表,…

我怀疑这是一个好主意。

用您的演示数据,“弓”是唯一不是别的东西的组成部分。 这是真的吗? 你怎么知道数组中的哪些元素与工作表中的哪一行有关?

也许更重要的是,下一步处理所需的数据分布在两个来源。 你可能会节省空间(数组会变小一些),但是这会让你的代码更加复杂和慢。 我记得当空间紧张的时候(我是程序员的第一台商用计算机的操作系统和16位用户的内存量在45到1000Kb之间),我们接受增加的复杂性和更慢的运行时间作为适合我们程序的必要价格进入可用的内存。 你不必做出牺牲。 一个简单的程序是写得更快,更容易维护,更可靠,所以开始简单。

†我不是在开玩笑, 我真的意思是最大的内存是1Mb。


我的理解是希望将工作表内的数据传输到内存中,以便处理更方便。 我发现很难看到你的数组是否可以方便任何事情。 创build它所需的处理也是复杂的。 你花了多长时间写ReDimPreserve

请考虑以下替代结构。


  | 1 | 2 | 3| 4| 5| 6| 7| 8| 9|10|11|12| --|-------|--------|--|--|--|--|--|--|--|--|--|--| 1|Bow |Crafted | 2| 2| 3| 1| 5| 3|10| 8| 6| 1| 2|Wood |Crafted | 4| 2|12| 9| 3|Rope |Crafted | 8|10| 4|Branch |Crafted |13| 1| 5|Crystal|Resource| 6|Plumes |Crafted | 7| 1| 7|Bird |Resource| 8|Web |Crafted |11| 5| 9|String |Resource| 10|Shard |Resource| 11|Spider |Resource| 12|Leaf |Resource| 13|Tree |Resource| 

这被称为一个不整齐的数组,因为每一行都是不同的长度。 这在逻辑上与工作表相同。 第1,2,4,6,8,10和12列中的值保持不变。 第3,5,7,9和11栏中的文字被行号所取代。 例如:“木”已被“2”替代,“绳”已被replace为“3”,其中“2”和“3”是持有木头和绳索的细节的行。 (我手工创build了这个表,但是我确信即使有错误,你也可以看到这个想法。

我希望你能看到,从弓到每个组件(木,绳,水晶,碎片和羽stream),从木材到其组件(分支和叶),并不困难。 我也希望你能看到将工作表转换为这个数组并不是什么大问题。

在这个阶段不要担心如何创build一个破旧的数组,而不是一个正方形或立方体数组。 在这个阶段,我希望你考虑一下数据结构。 获得正确的数据结构,程序结构将变得简单。 由于数据结构错误,程序难以编码。


上面的结构很简单,但不是自我logging。 第7栏是材料还是数量? 对于这个问题,结构自我logging可能并不重要,但对于更复杂的问题来说可能并不重要。

Long,String,Double和Boolean是编程语言自带的内在数据types。 通常这些内在的数据types是足够的,但有时却不是。 我所知道的所有通用语言都有一些从这些简单数据types构build更复杂数据types的方法。 大多数语言称这些复杂的数据types为“结构”,但VBA将其称为“用户types”。 考虑:

 Type tComponent RowMaterial As Long Quantity As Long End Type Type tMaterial Name As String Crafted As Boolean Component() As tComponent End Type 

Type xxxEnd Type的语句定义了一个用户types。 我似乎总是想使用一个types和一个variables相同的名称。 我的惯例之一是有一个types名称的领先“吨”。

我首先定义一个制作材料的组成部分。 一个组件对应于列(3,4),(5,6)等等。 然后我定义一个名称,一个布尔值来logging制作或资源和一个组件数组的材料。 如果材质是资源, Crafted将是FalseComponent将不会被使用。 如果材料是精心制作的,精心devise的将是TrueComponent将被适当地ReDimed保存和存储。

考虑Type tMaterial如何与工作表行相关。 列1包含名称,列2包含“特制”或“资源”。 我已经用一个布尔variablesreplace了第2列,但这只是编码相同信息的不同方式。 Type tComponent包含标识组件和数量的行号,与列对(3,4),(5,6)等相匹配。 最大的区别就是tMaterial是自我logging的。 如果你在六个或十二个月内回到这些macros,这两种方法哪一个更容易理解呢? 我相信方法2会更容易。 如果要维护一个macros或者其他程序来满足不断变化的需求,维护程序员的工作变得很容易是一个非常重要的考虑因素。 毕竟,你可能是那个维护程序员。

以下代码显示了如何使用这些用户types:

 Sub ShowConcept() Dim Materials() As tMaterial ReDim Materials(1 To 13) Materials(1).Name = "Bow" Materials(1).Crafted = True ReDim Materials(1).Components(1 To 5) Materials(1).Components(1).RowMaterial = 2 Materials(1).Components(1).Quantity = 2 Materials(1).Components(2).RowMaterial = 3 Materials(1).Components(2).Quantity = 1 Materials(1).Components(3).RowMaterial = 5 Materials(1).Components(3).Quantity = 3 ' : : : Materials(2).Name = "Wood" Materials(2).Crafted = True ReDim Materials(2).Components(1 To 2) Materials(2).Components(1).RowMaterial = 4 Materials(2).Components(1).Quantity = 2 Materials(2).Components(2).RowMaterial = 12 Materials(2).Components(2).Quantity = 8 ' : : : End Sub 

上面的两个数据结构在逻辑上是相同的; 他们只是演示了两种达到相同效果的方法。 尽pipe感觉是正确的,但我还没有精心testing过数据结构。 下一步是“使用”这个结构。 可能有必要修改,甚至放弃我的第一次尝试在适当的数据结构为您的问题,但我希望不是。


你需要三个macros。 您需要一个macros从原始工作表创build数组,另一个从数组创build新工作表。 使用演示数据,只有一个材料不是另一个的组成部分。 您可以创build一个macros来输出数组的第1行的组件(工作表的第2行)。 但是,您的真实数据可能有几个这样的“未使用”的材料,我想,您希望所有这些材料都输出到新的工作表中。 您需要一个控制macros来调用数组创buildmacros,然后调用每个未使用的材料的输出macros。

macros如何识别未使用的材料? 通过工作表和当前数组,不清楚哪些材料未被使用。 例如,第9行描述的材料是否被使用? 我需要看看所有其他的行。 只有在没有其他行使用第9行的材料时才会被使用。 我需要一个Type tMaterial的新属性:

 Type tMaterial Name As String Crafted As Boolean Used As Boolean Component() As tComponent End Type 

对于每种材料Used将有一个初始值为False。 当数组被build立时,通过将Used设置为True来logging对该材料的任何使用。

现在来devise我们的主要两个macros。


数组创buildmacros的第一步是将工作表导入到Variant 。 第一个数据行是2.您可以将最后一次使用的行标识为14.第一列是1.您可以将最后使用的列标识为12.单个语句将将此范围加载到创build数组的Variant 。 我将导入一个额外的空白列给予:

  | 1 | 2 | 3 | 4| 5 | 6| 7 | 8| 9 |10| 11 |12|13| --|-------|--------|------|--|----|--|-------|--|-----|--|------|--|--| 1|Bow |Crafted |Wood | 2|Rope| 1|Crystal| 3|Shard| 8|Plumes| 1| | 2|Wood |Crafted |Branch| 2|Leaf| 9| | | | | | | | 3|Rope |Crafted |Web |10| | | | | | | | | | 4|Branch |Crafted |Tree | 1| | | | | | | | | | 5|Crystal|Resource| | | | | | | | | | | | 6|Plumes |Crafted |Bird | 1| | | | | | | | | | 7|Bird |Resource| | | | | | | | | | | | 8|Web |Crafted |Spider| 5| | | | | | | | | | 9|String |Resource| | | | | | | | | | | | 10|Shard |Resource| | | | | | | | | | | | 11|Spider |Resource| | | | | | | | | | | | 12|Leaf |Resource| | | | | | | | | | | | 13|Tree |Resource| | | | | | | | | | | | 

我现在需要每行下行并检查第3,5,7,9和11列。任何材料名称都必须用相关行号replace。 由于这是一个变体数组,我可以用一个数字值replace一个string值。

例如,在元素R1C3中,我find“Wood”。 我需要在第1行中查找第2行中的“Wood”。我将R1C3设置为2,将R2C13设置为“U”以指示使用木材,

  | 1 | 2 | 3 | 4| 5 | 6| 7 | 8| 9 |10| 11 |12|13| --|-------|--------|------|--|----|--|-------|--|-----|--|------|--|--| 1|Bow |Crafted | 2| 2|Rope| 1|Crystal| 3|Shard| 8|Plumes| 1| | 2|Wood |Crafted |Branch| 2|Leaf| 9| | | | | | |U | 3|Rope |Crafted |Web |10| | | | | | | | | | 

我重复R1C5,我发现“绳”。 我在第一列中查找第三行中的“绳索”。我将R1C5设置为3,将R3C13设置为“U”,给出:

  | 1 | 2 | 3 | 4| 5 | 6| 7 | 8| 9 |10| 11 |12|13| --|-------|--------|------|--|----|--|-------|--|-----|--|------|--|--| 1|Bow |Crafted | 2| 2| 3| 1|Crystal| 3|Shard| 8|Plumes| 1| | 2|Wood |Crafted |Branch| 2|Leaf| 9| | | | | | |U | 3|Rope |Crafted |Web |10| | | | | | | | |U | 

要将原始工作表转换为此答案顶部的表单(第13列除外),我需要:

  • 每行(1到13)的外部循环。
  • 包含材料名称的每个列3,5的内部循环。
  • 内部循环search材料名称的行。

我不需要recursion来创build这个结构。 我可以在这个表单上使用修改后的数组,但是我相信如果数据被移动到一个Type tMaterial的数组,这将使新工作表创buildmacros更容易理解。


据我所知,有一个特定的工作表,你要输出值的专栏。 这个工作表的名称,列的字母/数字和第一行的编号可以被硬编码到macros中,被定义为常量或者是macros的参数。 我将忽略工作表和列,但会使行号成为macros的参数。

对于您可能想要的macros的第一行:

 Bow - 1 

我首先读到你的问题意味着你想要抑制这一行,但我不能确定这是否是正确的解释。 不pipe; 我将解释这一行可能被压制,或者与其他行稍后有所不同。

在第一行中,您需要列出Bow的组件:

 Bow - 1 >Wood – 2 >Rope – 1 »Crystal – 3 »Shard – 8 >Plumes – 1 

我使用“>”来表示缩进,因为我认为名称后面的连字符是实际的连字符。 1,2,1,3,8和1是数量。

在Wood的行下面,你想要列出它的组件的行,但是你想要数量乘以2,Woods的数量:

 Bow - 1 >Wood – 2 >>Branch – 4 >>Leaf – 18 >Rope – 1 >Crystal – 3 >Shard – 8 >Plumes – 1 

分支和叶子是资源,没有组件,但是,如果他们确实有组件,你会希望这些组件列在伍德的行下面。

这绝对是recursion将是最简单的技术的要求。

recursion例程(让我们称之为OutMatRow )将需要一些参数:

  • Materials :由第一个macros创build的数组。
  • RowMaterial :当前材质的材质内的行。
  • RowOutput :输出列中的行。
  • Quantity :当前材料的数量。
  • NumIndents :当前素材的缩进数量。

我说“参数”,但Materials可能是一个全局variables,因为OutMatRow不会改变这个数组。 RowOutput也可以是全局的,因为每次输出行时都会更新源variables。 RowMaterialQuantityNumIndents必须是参数,因为每个调用都需要自己的参数值。

控制程序将为每个未使用的材料调用OutMatRow 。 有了您的演示数据,唯一未使用的材料是弓,所以电话会是:

 Call OutMatRow(Materials, 2, X, 1, 0) 

其中X表示第一个输出行的编号。

OutMatRow中将会有很less的代码。

  • 材料的行必须输出。 NumIndentsMaterials(RowMaterial).NameQuantity给出了这一行的值。 如果需要,您可以使用不同的格式,或者在NumIndents = 0时取消输出。
  • RowOutput必须为下一个输出行准备好。
  • 对于制作材料的每个组成部分,例程将自行调用:

     Call OutMatRow(Materials, _ Materials(RowMaterial).Component(N).RowMaterial, _ RowOutput, _ Quantity * _ Materials(RowMaterial).Component(N).Quantity, _ NumIndents + 1) 

如果您对recursion例程不熟悉,则需要了解OutMatRow的调用OutMatRow

  • 控件macros为Bow调用OutMatRow
  • OutMatRow输出Bow的行,并自动调用Bow的第一个组件,即Wood。
  • OutMatRow输出Wood的行,并调用Wood的第一个分支。
  • OutMatRow输出分支的行。 分支没有组件,所以例程返回到调用者。
  • OutMatRow自称是Wood的第二个组件,就是Leaf。
  • OutMatRow输出Leaf的行。 Leaf没有组件,所以例程返回给它的调用者。
  • 木有没有更多的组件,所以例程返回到调用者。
  • OutMatRow自称是Bow的第二个组件,就是Rope。
  • 等等。

这将是很难让你的头。 试试我给出的解释。 如果你还在努力回答问题,我会尝试一个不同的解释。