【问题标题】:Strange behavior when assigning a VBA array to formulas of an excel range将 VBA 数组分配给 excel 范围的公式时的奇怪行为
【发布时间】:2013-10-08 03:57:48
【问题描述】:

这很奇怪......有人有一些知识或可以弄清楚为什么会发生这种情况?

过去,我能够将一组公式分配给 Excel 范围(快速分配示例: Range("A1:A1000") = ArrayOfFormulas)。与逐个单元格(示例单元格按单元格:Range("A" & i).Formula=ArrayOfFormula(i) 在循环内。很慢!)。

我现在正在使用 Excel 2013,当尝试将存储在字符串数组中的公式分配给 Excel 范围时,它不起作用(Excel 显示公式,但不显示左侧部分的计算下图)不幸的是我无法检索旧代码进行比较。例如,以下代码在 A1 到 A1000 范围内显示“=1+2”而不是“3”。

Sub AssignFormulas_1()
    Dim i as Long
    Dim FORML_ARRAY(1 To 1000, 1 To 1) As String
    For i = 1 To 1000
         FORML_ARRAY(i, 1) = "=1+2"
    Next i
    Range("A1:A1000").Formula = FORML_ARRAY  '<- Don't work as formula
                                             '   It put the value!
End Sub

但是,当我不使用公式数组时,它可以正常工作(根据下图右侧,所有单元格显示“3”而不是“=1+2”)。代码如下:

Sub AssignFormulas_2()
    Dim i as Long
    Dim FORML_SINGLE As String
    FORML_SINGLE = "=1+2"
    Range("A1:A1000").Formula = FORML_SINGLE '<- works ok, not practical for my
                                             '   real life case as I need 
                                             '   different formula for each cell.
End Sub

下面的代码也可以很好地工作(在这种情况下,我逐个单元格地分配公式,因此更灵活,但对于大型公式来说却很慢)。

Sub AssignFormulas_3()
    Dim i as Long
    Dim FORML_ARRAY(1 To 1000, 1 To 1) As String
    For i = 1 To 1000
         FORML_ARRAY(i, 1) = "=1+2"
         Range("A1:A" & i).Formula = FORML_ARRAY(i, 1) '<- works ok, but slowly
    Next i
End Sub

以上所有代码输出的图片:


注意:单元格的格式正确为数字,而不是文本!

【问题讨论】:

  • 没有 2013 年要检查,但我发现 this MSDN document 可能会有所帮助。
  • @Passerby,谢谢,但 FormulaArray() 用于其他用途。它适用于矩阵或 Excel 公式数组,但不适用于 VBA 数组。使用 FormulaArray() 您将在单元格中获得公式 {=Sum(A1:A3)} 而不是 =Sum(A1:A3)
  • 出于好奇,这什么时候会实用?您花时间用公式填充数组,将公式写入必须计算的范围?!?!为什么不简单地用公式填充一个范围?或者在数组中进行计算并用计算填充范围?你有什么理由为你的方法,而不是说它错了只是试图理解为什么。似乎在您填充数组的时间里您可以执行计算(比让公式计算更快),或者您可以完全跳过数组并直接添加公式。
  • @user2140261,谢谢。是的,我确实将其用于现实生活中的建议:用户体验,在不允许 VBA 时绕过某些安全限制,或用于审计帮助。 首先,关于你的观点是有值而不是公式,我通常更喜欢公式来查看结果的含义。如果您审核一个名为“没有商业成本的平均净利润率”的列,您更喜欢什么:只有结果值还是公式? (不会轻易看出“财盘”是不是“商业成本”的一部分,什么是“净值”,用什么来加权平均等等?)。
  • 第二,有时我只是使用“SUM”公式对特定单元格进行多个非常复杂的 vlookup 或嵌套 sumif(或者有时是不可能的 vlookup)(之前使用 VBA 搜索过)。如果您想要更改几个参数(但当然不是用于使用 VBA 搜索单元格的标准)进行敏感性分析,这会加快以后的计算。想象一下具有复杂 sumif 的 20.000 个单元格,仅从其他 20.000 个单元格中挑选 1 到 10 个单元格。好吧,也许不是做这个的软件,但是没有时间和预算来评估其他软件一次,一周后就忘记了。

标签: arrays performance vba excel


【解决方案1】:

您的第一个代码应该可以工作。
不要将其声明为字符串,而是将其声明为变体。

所以改变你的声明:

Dim FORML_ARRAY(1 To 1000, 1 To 1) As String

到这里:

Dim FORML_ARRAY(1 To 1000, 1 To 1) As Variant

我无法在 Excel 2013 上尝试它,但它应该可以工作。
希望是这样。

【讨论】:

  • +1 它适用于 Excel 2013。出于类型安全原因,我更喜欢使用我计划使用的较窄类型。在这种情况下,字符串是处理 Excel 公式的正确类型。我不喜欢 VBA 推动我使用变体,但我想在这种情况下无事可做......
  • @L42,谢谢!函数 #2 和 #3 确实有效,这很奇怪,在这些情况下不需要 Variant 声明(我仍然可以使用字符串声明)。你知道为什么吗??
  • @A.Sommerh 函数 #2 和 #3 有效,因为这是将公式分配给范围的默认方式。它接受字符串声明,因为 excel 确实希望公式为字符串格式。但是,当您将数组传递给函数 #1 中的范围时,excel 会假定您要保留数组数据类型。将数组传递给范围不同于将数组元素传递给范围。
  • @L42 谢谢,但仍然填充它不是一致的方式。在处理“.value”方法时我可以理解您的论点,但是为什么在使用“.function”方法时要保留数组数据类型呢?我知道这个问题没有答案,只是猜测(可能甚至 VBA 背后的原始微软团队都不知道原因;-))。你不需要回复这个!再次感谢您的帮助。
【解决方案2】:

有趣的问题。我以前从未使用过此功能。

您的第一个在 2010 年和 2013 年对我来说具有相同的结果 - 包含字符串 "=1+2" 的列。我将FORML_ARRAY 从字符串更改为Variant,它按预期工作。为了更好地衡量,我将公式更改为包含i,它仍然用公式填充范围:

Sub AssignFormulas_1()
    Dim i As Long
    Dim FORML_ARRAY(1 To 1000, 1 To 1) As Variant 'changed to Variant
    For i = 1 To 1000
         FORML_ARRAY(i, 1) = "=" & i & "+2" 'changed 1 to i - still works
    Next i
    Range("A1:A1000").Formula = FORML_ARRAY
End Sub

【讨论】:

  • @Doug,谢谢!函数 #2 和 #3 确实有效,这很奇怪,在这些情况下不需要 Variant 声明(我仍然可以使用 String 声明)。你知道为什么吗??你如何找出函数#1(教授和错误)的解决方案?你以前没试过吗?做一些性能测试,当你必须使用大量单元格时,它真的会让你的 VBA 代码飞起来!!
  • 虽然我没有用这种方式填写公式,但我已经在范围和数组方面做了很多工作,并且根据经验,我认为Variant 会起作用。我在 2010 年和 2013 年首先对其进行了测试,两者的结果相同。
【解决方案3】:

只是添加另一个选项,因为正如您所说,您宁愿正确调暗阵列,这样您就可以,它只增加 1/250 秒到 OVERALL 子 (不是每个循环),但允许您将数组调暗为字符串。

Sub AssignFormulas_2()
    Dim i As Long

    Dim FORML_ARRAY(1 To 1000, 1 To 1) As String

    For i = 1 To 1000
         FORML_ARRAY(i, 1) = "= " & i & " + 2"
    Next i

    Range("A1:A1000").Formula = FORML_ARRAY  '<- Don't work as formula
                                             '   It put the value!

    Range("A1:A1000").Value = Range("A1:A1000").Value
End Sub

现在,我不确定为什么 excel 将字符串读取为常量,但是如果您运行原始公式并转到 Evaluate Formula,它会给出单元格包含常量的错误,这似乎只是一个错误,但如果我想出别的办法,我也会编辑我的答案。

注意:此答案并非旨在作为公认答案的更好答案,它是一个较慢的答案,更像是一种替代方案;回答您想要使数组变暗的请求。

更新:此方法将分割接受的答案与上述方法之间的差异。这只会增加子运行时间的 1/500 秒。

Sub AssignFormulas_Transpose()
    Dim i As Long

    Dim FORML_ARRAY(1 To 1000) As String
    For i = 1 To 1000
         FORML_ARRAY(i) = "= " & i & " + 2"
    Next i

    Range("A1:A1000").Formula = Application.Transpose(FORML_ARRAY)  '<- Don't work as formula
                                                                    '   It put the value!
End Sub

【讨论】:

  • 谢谢,但 TextToColumns 不适用于所有情况。如果您的公式文本包含“,”或“;”它可以将公式拆分到右侧的单元格,从而破坏解决方案。这对我来说是一个交易破坏者。
  • 您能否替换您的解决方案并使用Range("A1:A1000").value=Range("A1:A1000").value 而不是TextToColumns?会做出TextToColumns的把戏,无论如何不要拆分公式,我相信会更快!
  • 更新了答案,这比 TextToColumns 快得多,我建议将 TextToColumns 按单元格的最大大小设置为宽度,不使用分隔符进行分隔,但值更快,并且对于记录它只比使用变体数组慢 1/250 秒。
  • @A.Sommerh 添加了另一种方法,仍然不如接受的答案快,但仍然允许您将数组调暗为字符串数组,并减少我的原始方法和接受的方法减半(它只比接受的答案慢 1/500 秒)。
  • 很高兴有一个额外的方法。谢谢!但是,请注意:我刚刚读到Application.Transpose 对元素的数量有限制(Excel 2002 之前的5461 元素和 2^16,对于 Excel 2002、2007 和 2010 使用时为 65536一维数组)。 参见#1: link 参见#2: link
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-11
  • 2014-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多