如何(如果可能)从 VBA Excel 2010 的工作表中定义和填充全局数组变量?

Posted

技术标签:

【中文标题】如何(如果可能)从 VBA Excel 2010 的工作表中定义和填充全局数组变量?【英文标题】:How (if possible) do I define and populate a global array variable from a worksheet in VBA Excel 2010? 【发布时间】:2015-05-19 20:35:03 【问题描述】:

*******背景******* 出于没有争议的原因,我正在使用 Excel 2010 和 VBA 来编写一个科学模型,该模型涉及用户从在线数据库中为所涉及的每个物种将数据输入到工作表中。物种的数量可能会因模拟而异。随着程序的迭代,它会多次调用许多函数(有些是数万次)。该程序的执行速度变得太长,所以我想尝试加快它。在我看来,提高执行速度的两件简单的事情是减少工作表调用的数量和最小化我必须使用的变体数量。

另外值得注意的是,几个函数共享相同的常量。

我在 stackexchange 和其他网站上查看了其他地方,但我仍然没有找到我要找的东西 ♪。也许我只是没有正确的搜索条件。


问题 因为物种的数量不是恒定的,并且因为我想让一些常量数组可用于多个函数,所以我可以定义一个全局变量,它是一个双(或单)精度数组,当一个sub 运行,从excel表中读取一次常量,然后在我的“main” sub 完成执行时被销毁?

我可以在主子程序中创建数组并将其作为参数传递,但我的一些函数已经将其他函数作为参数调用,而且我的几行代码非常长且难以阅读。通过将这些常量参数传递给函数只会增加这些行的长度。

第二个问题,如果我不能创建全局数组变量,是否可以调用工作表一次(就像我所做的那样),但要使变量成为双精度变量而不是变量?由于类型不匹配错误,我无法使以下工作。 将 C() 调暗为 Double redim c(1 到 7, 1 到 n) C = Application.Transpose(Worksheets("Viscosity2").Range("J10:p19"))

函数示例:我有一个在 VBA 中运行的函数。在我的程序过程中,这个函数被调用了数万次。我想让 C(1 到 7, 1 到 n) 数组和 mw(1 到 n) 数组双精度数组调用一次工作表,然后可用于多个函数。

下面是示例函数:

Function mumx(y, T)
    'this function calculates the mixture viscosity using the Chapman Enskog Wilke method 
    'using the mol fraction vector, y, and the temperature T

    n = UBound(y, 1) - LBound(y, 1) + 1  'number of species


'***********Get Equation Parameters from Worksheet**************
Dim C() As Variant
C = Application.Transpose(Worksheets("Viscosity2").Range("J10:p19"))
Dim mw As Variant
mw = Application.Transpose(Worksheets("Viscosity2").Range("g10:g19"))
'***************************************************************

Dim mu() As Double
ReDim mu(1 To n)


For i = 1 To n Step 1
    mu(i) = (C(1, i) * (T ^ C(2, i))) / (1 + C(3, i) / T + (C(4, i) / (T ^ 2)))
Next i


Dim phi() As Double
ReDim phi(1 To n, 1 To n)

For i = 1 To n
    For j = 1 To n

        phi(i, j) = 1 / 8 ^ 0.5 * (1 + mw(i) / mw(j)) ^ -0.5 * (1 + (mu(i) / mu(j)) ^ 0.5 * (mw(j) / mw(i)) ^ 0.25) ^ 2

        test = 1
    Next j
  Next i

Dim denom As Double
Dim mumix As Double
denom = 0
mumix = 0
For i = 1 To n
    For j = 1 To n

        denom = denom + y(j) * phi(i, j)
    Next j


    mumix = mumix + y(i) * mu(i) / denom
    denom = 0
Next i



mumx = mumix
'where the units on mumx are in units of cP (which are 1 gm/(m*s))

End Function


    '************Example constants are as follows********
    'PS should someone stumble on this looking for say viscosity data 
    'the following constants just example constants
    '
    '
    'C(1, 1) = 0.00018
    'C(1, 2) = 0.000017
    'C(1, 3) = 0.001113
    'C(1, 4) = 0.00215
    'C(1, 5) = 0.0005255
    'C(1, 6) = 0.0011
    'C(1, 7) = 0.0006559
    'C(1, 8) = 0.00005
    'C(1, 9) = 0.00026
    'C(1, 10) = 0.002079
    '
    'C(2, 1) = 0.69
    'C(2, 2) = 1.115
    'C(2, 3) = 0.534
    'C(2, 4) = 0.46
    'C(2, 5) = 0.59
    'C(2, 6) = 0.563
    'C(2, 7) = 0.608
    'C(2, 8) = 0.90
    'C(2, 9) = 0.68
    'C(2, 10) = 0.4163
    '
    'C(3, 1) = -0.59
    'C(3, 2) = 0
    'C(3, 3) = 94.7
    'C(3, 4) = 290.
    'C(3, 5) = 106.
    'C(3, 6) = 96.3
    'C(3, 7) = 54.7
    'C(3, 8) = 0
    'C(3, 9) = 98.9
    'C(3, 10) = 353.
    '
    'C(4, 1) = 140.
    'C(4, 2) = 0
    'C(4, 3) = 0
    'C(4, 4) = 0
    'C(4, 5) = 0
    'C(4, 6) = 0
    'C(4, 7) = 0
    'C(4, 8) = 0
    'C(4, 9) = 0
    'C(4, 10) = 0
    '
    '
    'C(5, 1) = 0
    'C(5, 2) = 0
    'C(5, 3) = 0
    'C(5, 4) = 0
    'C(5, 5) = 0
    'C(5, 6) = 0
    'C(5, 7) = 0
    'C(5, 8) = 0
    'C(5, 9) = 0
    'C(5, 10) = 0
    '
    'C(6, 1) = 300
    'C(6, 2) = 300
    'C(6, 3) = 300
    'C(6, 4) = 300
    'C(6, 5) = 300
    'C(6, 6) = 300
    'C(6, 7) = 300
    'C(6, 8) = 300
    'C(6, 9) = 300
    'C(6, 10) = 300
    '
    'C(7, 1) = 1000
    'C(7, 2) = 1000
    'C(7, 3) = 1000
    'C(7, 4) = 1000
    'C(7, 5) = 1000
    'C(7, 6) = 1000
    'C(7, 7) = 1000
    'C(7, 8) = 1000
    'C(7, 9) = 1000
    'C(7, 10) = 1000
    '
    '
    '
    'mw(1) = 2.0158
    'mw(2) = 18.0148
    'mw(3) = 28.01
    'mw(4) = 44.009
    'mw(5) = 16.0426
    'mw(6) = 31.998
    'mw(7) = 28.014
    'mw(8) = 44.0962
    'mw(9) = 30.0694
    'mw(10) = 28.0536
    '
    ''******************************

【问题讨论】:

【参考方案1】:

是的,您可以并且应该* 使用数组来存储用户输入的常量,是的,您可以将其设为全局,这样就不必将其传递给其他函数。

这是一个例子;请注意,首先将数据读入 Variant,然后将其传输到数组 - 这是导致类型不匹配错误的缺失步骤。虽然这看起来可能需要太多代码,但将数据传输到 Double 数组将比逐个读取单元格快很多倍。

Public C() As Double

Public Sub PopulateArrayC(n As Integer)

    ReDim C(1 To 7, 1 To n)

    Dim v As Variant
    v = Application.Transpose(Worksheets("Viscosity2").Range("J10:P" & n + 10 - 1))

    Dim i As Integer, j As Integer
    For i = 1 To 7
        For j = 1 To n
            C(i, j) = v(i, j)
        Next j
    Next i

End Sub

*读取和写入单元格非常耗时。尽可能将读写次数限制为*

    将常用值存储在变量中,以及 一次读取/写入整个范围。

【讨论】:

感谢您的回复。我基本上按照你上面展示的那样做了,它有效。我有一个后续问题:我知道全局变量通常是禁止的。我可以通过在我的主程序中声明“C”数组并根据需要将其作为参数传递给每个函数来实现基本相同的结果。我检查了这可能会做什么,我最长的代码行是大约 575 列宽。避免全局变量并将我的“C”数组传递给每个函数会更好吗?我仍然会创建一个“PopulateArray”子并在开始时通过“C”数组 ByRef 来执行它以填充它。 PS 对于一个简单的测试用例,我的代码过去在大约 690 秒内执行,但现在它在大约 350 秒内执行。当我处理较大的案例时,这将节省大量时间。再次感谢。 我的建议是不要太拘泥于“从不使用全局变量”。让你的代码进入工作状态,提高性能,然后担心一个全局变量是否是一个罪过。关于 500 多个字符宽的代码 - 使用 line continuation character!这是迄今为止更大的罪过,因为它使您的代码几乎无法阅读。也就是说,即使您将该代码包装成多行,我建议您将逻辑分解为更小的块,以使您的代码更易于管理。 我知道换行符,并且我已经在我的代码的各个部分使用了它。我觉得在我的某些情况下,这让这些线条更难理解,因为我的函数有很多参数,其中很多都有自己的参数。结果是我将一个函数分成两半,将部分参数放在下一行。

以上是关于如何(如果可能)从 VBA Excel 2010 的工作表中定义和填充全局数组变量?的主要内容,如果未能解决你的问题,请参考以下文章

如何对 Excel VBA 代码进行单元测试

如何使用 Excel VBA 将我的所有 lync 2010 联系人姓名传输到 Excel?

VBA - 如何在 Excel 2010 的目录中获取最后修改的文件或文件夹

从 Access 2010 VBA 打开 Excel 2010 文件

从 Access 2010 VBA 控制 Excel 工作簿

Excel 2010 VBA:使用单元格中的值保存文件以确定路径和文件名