从工作表将二维数组传递给 VBA/UDF 函数
Posted
技术标签:
【中文标题】从工作表将二维数组传递给 VBA/UDF 函数【英文标题】:Passing 2D-array to VBA/UDF function from sheet 【发布时间】:2017-12-08 01:34:40 【问题描述】:我使用的是 Excel 2010。
当 VBA 用户定义函数(例如 make2DArray)输出表示二维数组的变量时,该二维数组可以用作另一个 VBA 函数(例如 someFunction)的输入。从 VBA 代码中调用函数时。它工作正常。
但是,在执行相同操作时,从工作表内(通过单元格中的公式)将 make2DArray 输出作为参数传递给 someFunction,那么它似乎仅适用于具有 **2 或更多行**的二维数组。生成 1 行的二维数组将失败。看来 1-row 2D-array 随后会自动转换为等效的 1D-array。
一个小例子:
Option Explicit
'returns last (top-most, right-most) element
Function someFunction(v As Variant) As Integer
On Error Resume Next
Debug.Print "Dim-1 size", UBound(v, 1) - LBound(v, 1) + 1
Debug.Print "Dim-2 size", UBound(v, 2) - LBound(v, 2) + 1
On Error GoTo 0
someFunction = v(UBound(v, 1), UBound(v, 2))
End Function
Function make2DArray(h As Integer, w As Integer) As Variant
Dim i As Integer, j As Integer
Dim v2d As Variant
ReDim v2d(1 To h, 1 To w)
For i = 1 To h
For j = 1 To w
v2d(i, j) = i * j
Next j
Next i
make2DArray = v2d
End Function
Sub test()
'also works when called from sheet
Debug.Print someFunction(make2DArray(2, 3)) 'returns 6
'doesn't work when called from sheet
Debug.Print someFunction(make2DArray(1, 3)) 'returns 3
End Sub
测试功能可以在 VBA 中正常工作。类似地 =someFunction(make2DArray(2, 3)) 或任何 =someFunction(make2DArray(i, j)) 单元格公式适用于 i>1,但是 =someFunction(make2DArray(1, 3)) 或任何 =someFunction(make2DArray (1, j)) 只会产生一个#VALUE!结果在工作表中。
我的问题:这种行为是否记录在某处?对于 1 行二维数组,有没有办法避免从二维数组“转换”到一维数组?
【问题讨论】:
为避免混淆,您可能需要交换 w 和 h。传回工作表的二维数组将第一个等级视为 行 的数量,将第二个等级视为 列 的数量。 刚刚测试了你的=someFunction(make2DArray(2, 4))
,它返回8,这是两个等级的ubound的值。
是的,当make2DArray(1,4)
的输出被传递回 Excel 时,Excel 将其转换为一维数组,然后将该一维数组传递给 someFunction
函数。将 UDF 的 Excel 修改结果用作 UDF 的参数总是一个坏主意。而是将h
和w
传递给someFunction
本身,并让someFunction
调用make2DArray
。 (即称为=someFunction(1, 4)
。)
tbh,我以前从未见过。就像使用转置将二维(一行)数组转换为一维数组一样。我认为这是一个错误,因为 VBA 开销正在查看具有一个“行”的二维变体数组(例如 1 作为 ubound 第一等级)并将其转换为以原始 ubound 第二等级作为 ubound 的一维数组唯一的一维等级。为了普遍性,我能提出的唯一建议是错误控制;我将在下面发布一个建议。
fwiw 谢谢! 提出了一个关于编程的智能且有据可查的问题。似乎最近对excel 标签进行的十分之九的查询只不过是带有蹩脚的“我是菜鸟,不知道该怎么做”叙述的软件规范.
【参考方案1】:
来自我的cmets:
我以前从未见过。就像使用转置将二维(一行)数组转换为一维数组一样。我认为这是一个错误,因为 VBA 开销正在查看具有一个“行”的二维变体数组(例如 1 作为 ubound 第一等级)并将其转换为以原始 ubound 第二等级作为 ubound 的一维数组唯一的一维等级。为了普遍性,我能提出的唯一建议是错误控制;我会在下面发表一个建议。
这里有一些错误控制来克服流氓二维到一维数组的转换。
'returns bottom-right element of 2D-array
Function someFunction(v As Variant) 'As Integer
On Error Resume Next
Debug.Print "Dim-1 size", UBound(v, 1) - LBound(v, 1) + 1
Debug.Print "Dim-2 size", UBound(v, 2) - LBound(v, 2) + 1
On Error GoTo 0
'Debug.Print IsArray(v)
'Debug.Print UBound(v, 1) & ":" & UBound(v, 2)
On Error GoTo err_1D_array
someFunction = v(UBound(v, 1), UBound(v, 2))
Exit Function
err_1D_array:
someFunction = v(UBound(v))
End Function
【讨论】:
我真的很好奇这个 bug 是否比典型的应用程序更高效 CPU . 就此而言,与标准的 application.transpose(application.transpose(v)) 相比,当第二个 rank 的 ubound 为 1 时,它也可能更有效。 Transpose 是一个非常有效的函数,但充其量它确实涉及创建数组的副本,因此会产生成本。在最坏的情况下,它涉及 Excel 和 VBA 之间的缓慢往返 - 我从未发现转置比直接访问数组更快的情况(但这种情况可能存在!) 我同意错误控制确实有效并有助于规避数组大小问题。在接受这个答案之前,我会再等一会儿,以防有人知道这种行为是否记录在某处(我想了解它是否有原因,或者它是否只是一个“错误”)【参考方案2】:UDF 应该真正使用参数进行复制,这些参数可以是范围、向量、数组、标量常数或来自其他函数的数组/向量输出。 SomeFunction(1,2,3) 获取一维向量数组 SomeFunction(1;2;3) 获取二维数组 SomeFunction(Range as variant) 获取包含范围的变量其 Value2 属性始终返回标量或二维数组的对象。
【讨论】:
以上是关于从工作表将二维数组传递给 VBA/UDF 函数的主要内容,如果未能解决你的问题,请参考以下文章