【问题标题】:VBA - Including own function within Evaluate()VBA - 在 Evaluate() 中包含自己的函数
【发布时间】:2021-09-07 10:24:10
【问题描述】:

在 VBA 中,我习惯于使用 Evaluate() 计算一列的值,这是另一列的函数,以便不使用循环。例如,要评估第 2 列,其值是第 1 列值的指数,我会写(这里限于前 10 行):

Dim rng1 As Range
Dim rng2 As Range

Set rng1 = Worksheets("Sheet1").Range(Cells(1,1),Cells(10,1))
Set rng2 = Worksheets("Sheet1").Range(Cells(1,2),Cells(10,2))

rng2 = Evaluate("EXP(" & rng1.Address & ")")

我现在想以同样的方式处理我自己的函数。为了进行测试,我创建了一个返回输入指数值的函数:

Function TestExpo(alpha) As Double
    TestExpo = EXP(alpha)
End Function

我以与之前 Evaluate() 所述相同的方式进行:

rng2 = Evaluate("TestExpo(" & rng1.Address & ")")

我没有收到错误,但 Excel 工作表上什么也没发生。例如,我试图通过在函数名称中输入错误来触发错误:

rng2 = Evaluate("TestEx(" & rng1.Address & ")")

实际上,我在工作表上 rng2 的每个单元格处都出现了“名称”错误。因此,调用函数“TestExpo”有效。我还尝试使用实数而不是 rng1.Address:

rng2 = Evaluate("TestExpo(" & 2 & ")")

并且我在工作表上 rng2 的所有单元格中都得到了正确的 exp(2) 值,因此调用函数并取回单元格中的值就是这样。

我尝试以不同的方式指定输入和输出的类型。例如:

Function TestExpo(alpha) ' No error, but no values on the sheet
Function TestExpo(alpha) As Range ' No error, but no values on the sheet
Function TestExpo(alpha) As Variant ' No error, but no values on the sheet
Function TestExpo(alpha As Range) ' No error, but no values on the sheet
Function TestExpo(alpha As Variant) ' No error, but no values on the sheet
Function TestExpo(alpha As Double) ' Value error for each cell of the range rng2

除了上次使用 alpha As Double 进行的测试外,我没有收到任何错误,但 Excel 表上也没有任何值。

我的想法已经不多了......我试图找到VBA的EXP()函数的代码,以查看如何指定输入和输出,认为如果我在函数中使用相同的类型和参数“ TestExpo”,它可能工作,但我找不到EXP()函数的代码,它可能不是公开的。

有没有人遇到过类似的情况并解决了?

亲切的问候 泽维尔


更新:

感谢您的贡献!我继续前进!

当我使用时,正如@FaneDuru 所建议的那样

rng2 = Evaluate("""=TestExpo(" & rng1.Address & ")""")

每个单元格中都有一个值错误。检查工作表中的单元格,我可以看到每个单元格都归因于公式:

"=@TestExpo($A$1:$A$35)"

我和写作比较

rng2 = Evaluate("""=EXP(" & rng1.Address & ")""")

哪个有效,并归因于每个单元格的公式

"=EXP(@$A$1:$A$35)"

所以@出现在不同的位置。我手动将公式“=@TestExpo($A$1:$A$35)”更正为“=TestExpo(@$A$1:$A$35)”,它成功了!

所以问题是@的位置,我不明白为什么@在使用函数EXP时默认放在括号内的范围前面,但在使用我自己的函数时放在函数名前面.

尽管如此,手动更正单元格中的公式并不方便。受@FaneDuru 提议的启发,我通过以下方式在范围名称后添加 .Cells(1,1).Address(0,0) 来更正我的代码:

rng2 = Evaluate("""=TestExpo(" & rng1.Cells(1,1).Address(0,0) & ")""")

这成功了!

现在,第一个单元格中的公式如下所示:

"=@TestExpo(A1)"

并且单元格地址适应每一行(第2行公式为“=@TestExpo(A2)”,第3行“=@TestExpo(A3)”等)

@ 仍然出现在函数名称的前面,但括号内有一个单元格名称而不是范围,这使得公式有效。

现在我不得不承认我并不真正了解代码语法是如何工作的。事实上,添加 .Cells(1,1).Address(0,0),我希望 rng2 的所有单元格都将引用范围 rng1 的 Cell(1,1),但公式却适应了每个单元格以正确的方式。

所以,我最初的问题已经解决了:

rng2 = Evaluate("""=TestExpo(" & rng1.Cells(1,1).Address(0,0) & ")""")

甚至更简单:

rng2 = "=TestExpo(" & rng1.Cells(1,1).Address(0,0) & ")"

尽管如此,我还是很想了解代码语法是如何工作的——即,如果代码明确引用了 rng1.Cells(1,1).Address(0,0) 中的单元格(1,1),它是如何工作的可能 rng2 的所有单元格都没有引用单元格(1,1),而是引用了正确的单元格(单元格(2,1)、单元格(3,1)等)?

而且我还想了解为什么 @ 在使用函数 EXP 时默认放在范围前面,但在使用我自己的函数时放在函数名前面。有人有想法吗?

【问题讨论】:

  • 您确定rng2 = Evaluate("EXP(" & rng1.Address & ")") 按预期工作吗?我刚试了一下,它在所有单元格中输入了第一个值。
  • 好的...我再看看。不确定结果是否会因 Excel 版本而异。与此同时,你可能想试试我的回答。毕竟它可能会做你想做的事。
  • 这真的很奇怪...我刚刚复制并粘贴了您的确切代码,并且在 A1:A10 中输入了数字 1-10。 B1:B10 中的结果是所有十个单元格中的第一个结果 (2.71828182845905)。
  • 感谢您的反馈!这是我的代码,它确实对我有用。见下表: Column1:函数的输入,Col2:使用 Sub TestFR2 (VBA) 的输出,Col3:使用 Excel 公式 =EXP(A1) 复制到其他单元格的输出。 Col 2 和 3 相等。 Sub TestdFR2() Worksheets("sheet1").Activate Dim rng1 As Range Dim rng2 As Range Set rng1 = Worksheets("sheet1").Range(Cells(1, 1), Cells(10, 1)) Set rng2 = Worksheets ("sheet1").Range(Cells(1, 2), Cells(10, 2)) rng2 = Evaluate("exp(" & rng1.Address & ")") End Sub 0 1 1 0.1 1.11 1.11 0.2 1.22 1.22 0.3 1.35 1.35 0.4 1.49 1.49
  • 进行这样的重大更改(至少是有意的)是不寻常的(但并非未知)。但也许,他们认为它非常微妙,不会对绝大多数用户造成问题。很抱歉,我无法提供更多帮助。我希望我的建议,至少能给你一些尝试的选择。

标签: excel vba function evaluate


【解决方案1】:

不幸的是,我认为你不能以这种方式做你想做的事。 我认为您的示例没有达到您的预期。

rng2 = Evaluate("EXP(" & rng1.Address & ")")

将返回一个值数组,但只会将第一个值输入到 rng2 中的每个单元格中。 所以,即使你可以模仿 EXP 正在做的事情,它仍然不会做你想做的事情。

但是,如果您仍想模仿使用 EXP 函数获得的(可能不正确的)功能:

EXP 和你的 UDF 的区别在于 EXP 函数可以处理和返回数组,而你的 UDF 不能。

这是一个非常快速的功能,可以(并且应该)显着改进,但它可能会给你一个起点。

Public Function test(a As Variant) As Variant
    Dim arr() As Double
    If IsNumeric(a) Then
        ReDim arr(1 To 1)
        arr(1) = Exp(a)
    Else
        ReDim arr(1 To a.Count)
        Dim i As Long
        For i = 1 To a.Count
            arr(i) = Exp(a(i))
        Next i
    End If
    test = arr
End Function

更新:

显然,工作表公式中使用的 EXP 函数与 VBA 版本之间似乎也存在差异。

VBA 版本只接受一个单元格或值,而工作表版本能够接受值的范围/数组。 通常,您可以使用Application.WorksheetFunction 访问工作表版本,但不幸的是,这似乎不适用于EXP

【讨论】:

    【解决方案2】:

    要评估的函数必须是字符串(在双引号之间)...

    请尝试

    rng2 = Evaluate("""=TestEx(" & rng1.Address & ")""")
    

    已编辑

    请尝试下一个(经过测试的)示例:

    Function myFuncX(x As Long) As Long
       myFuncX = x + x * 2
    End Function
    

    测试子评估它。当然,你必须用适当的长值来感受“J2:J4”......

    Sub testEvaluateCustFunc()
        Dim rng As Range, rng2 As Range
        Set rng = Range("I2:I4"): Set rng2 = Range("J2:J4")
        rng.Value = Application.Evaluate("""=myFuncX(" & rng2.cells(1, 1).Address(0, 0) & ")""")
    End Sub
    

    已编辑

    请尝试了解下一次测试中会发生什么Sub。该方法能够评估公式、名称、范围、对象及其属性,返回对象、值或数组:

    Sub testEvaluate()
      Dim rng As Range, rng2 As Range, testArr As Variant
      Set rng = Range("I2:I4"): Set rng2 = Range("J2:J4")
    
      'Application.Evaluate can simple be replaced by using []:
      testArr = [J2:J4]: Debug.Print Join(Application.Transpose(testArr), "||") 'Transpose is necessary to convert in 1D array
                                                                                                     'able to be shown in Immediate Window (IW)...
      testArr = Application.Evaluate("Index(" & rng2.Address(0, 0) & ",)")  'in this way an array of the range values is built
      Debug.Print Join(Application.Transpose(testArr), ",")                       'here the array can be visualized in IW
      testArr = Evaluate("Row(1:5)")                         'A nice way of making an array of numbers using the Row property
      Debug.Print Join(Application.Transpose(testArr), "|")  'See the resulted array in IW
      'the next line evaluates the function for all rng range, incrementing the function argument addresses:
      'I did not see your function and I used this way ONLY TO USE THE FIRST CELL IN THE RANGE TO BE INCREMENTED
      rng.Value = Application.Evaluate("""=myFuncX(" & rng2.cells(1, 1).Address(0, 0) & ")""")
      Debug.Print rng2.Address, rng2.Address(0, 0) ' See here the difference between absolute and relative address
                                                                   'Only a relative address/reference can be incremented!
      'trying the absolute reference, will produce a wrong result, the formula using only the first cell of the range (absolute address):
      rng.Value = Application.Evaluate("""=myFuncX(" & rng2.cells(1, 1).Address & ")""")
      testArr = rng.Value
      Debug.Print Join(Application.Transpose(testArr), " ") 'the function is using only the first range cell
      'Now, the same thing using the first cell addres. Because I know what parametes the function uses...
      rng.Value = Application.Evaluate("""=myFuncX(" & Range("J2").Address(0, 0) & ")""")
      testArr = rng.Value
      Debug.Print Join(Application.Transpose(testArr), "|") 'if using the absolute address, the result will allways refer only
                                                                            'the first range cell, not being able to increment an absolute ref...
    End Sub
    

    如果您的函数应该接受一个包含许多单元格的范围,它可以按照我最初的建议工作。例如,使用下一个函数:

    Function myFunc2(rng As Range) As Long
       myFunc2 = rng.cells.count * 2 + Len(rng.cells(1, 1).Value)
    End Function
    

    您可以在测试中使用下一个构造Sub

    Sub testmyFunc2()
        Dim rng3 As Range: Set rng3 = Range("F2:F4")
        Dim rng2 As Range: Set rng2 = Range("J2:J4")
        Debug.Print myFunc2(Range("J2:J4"))
        rng3.Value = Evaluate("""=myFunc2(" & rng2.Address & ")""")
    End Sub
    

    我希望现在更清楚一点......如果仍然有一些模糊不清,请不要犹豫,要求澄清。但准时,在一个特定的方面。 Evaluate 方法的使用方法非常复杂...

    【讨论】:

    • 感谢您的回答!它实际上并没有解决问题。按照您的建议,rng2 范围的所有单元格都被赋予以下文本“TestExpo($A$1:$A$10)”,表示函数的名称和括号之间的 rng1 范围。
    • @Xav 然后,测试更新的答案。 VBA 我不明白这是一个要评估的公式。我只在函数前面添加了一个“=”字符。它应该工作。我以类似的方式多次使用Evaluate 自定义函数...
    • 我也试过这个,但它会在每个单元格中产生一个值错误,因为每个单元格都被赋予以下字符串“=@TestExpo($A$1:$A$35)”,Excel 无法理解我认为,因为 TestExpo 不是内置函数。我还尝试从公式中删除“@”,但没有帮助。
    • @Xav 我找不到你。理论上,VBA 应该使用rng.Value 属性。不应该写任何公式。我现在必须离开我的办公室。我将发布一个工作示例。如果你需要感觉不是一个范围(比如rng2)......
    • @Xav 好吧,Evaluate 方法很强大,但只有很好理解...我会尝试编辑答案并编写更复杂的测试Sub,我希望是更多关于 Evaluate 工作方式的 elocvent...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-05
    • 2013-07-18
    相关资源
    最近更新 更多