从工作表将二维数组传递给 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 行二维数组,有没有办法避免从二维数组“转换”到一维数组?

【问题讨论】:

为避免混淆,您可能需要交换 wh。传回工作表的二维数组将第一个等级视为 的数量,将第二个等级视为 的数量。 刚刚测试了你的=someFunction(make2DArray(2, 4)),它返回8,这是两个等级的ubound的值。 是的,当make2DArray(1,4) 的输出被传递回 Excel 时,Excel 将其转换为一维数组,然后将该一维数组传递给 someFunction 函数。将 UDF 的 Excel 修改结果用作 UDF 的参数总是一个坏主意。而是将hw 传递给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 函数的主要内容,如果未能解决你的问题,请参考以下文章

无法将二维数组传递给 C++ 中的辅助函数

JNI维数组:将二维数组从java传递给c

将二维数组传递给 double function_name 时出错

如何将二维数组传递给函数[重复]

将二维数组信息传递给 c# 中的方法

无法将二维字符数组传递给函数(C++)