VBA - 在 Evaluate() 中包含自己的函数
VBA - Including own function within Evaluate()
在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 & ")")
我现在想以同样的方式处理我自己的功能。为了进行测试,我创建了一个函数,该函数 returns 输入的指数值:
Function TestExpo(alpha) As Double
TestExpo = EXP(alpha)
End Function
并且我按照之前使用 Evaluate() 所述的相同方式进行操作:
rng2 = Evaluate("TestExpo(" & rng1.Address & ")")
我没有收到错误,但 Excel sheet 上什么也没发生。我试图通过在函数名称中输入错误来触发错误,例如:
rng2 = Evaluate("TestEx(" & rng1.Address & ")")
我实际上在 sheet 上的 rng2 的每个单元格中都遇到了“名称”错误。因此,调用函数“TestExpo”起作用。我还尝试使用实数而不是 rng1.Address:
rng2 = Evaluate("TestExpo(" & 2 & ")")
并且我在 sheet 上的 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 sheet.
上也没有任何值
我运行没主意了...我试图找到VBA的EXP()函数的代码,看看如何指定输入和输出,认为如果我在函数“TestExpo”中使用相同的类型和参数,它可能会工作,但我找不到 EXP() 函数的代码,它可能不是 public.
有没有人遇到过类似情况并解决了?
亲切的问候
泽维尔
更新:
感谢您的贡献!我继续前进!
当我使用时,正如@FaneDuru所建议的那样
rng2 = Evaluate("""=TestExpo(" & rng1.Address & ")""")
我在每个单元格中都遇到了值错误。检查工作中的单元格sheet,我可以看到每个单元格都归因于公式:
"=@TestExpo($A:$A)"
我比较写作
rng2 = Evaluate("""=EXP(" & rng1.Address & ")""")
哪个有效并将公式归因于每个单元格
"=EXP(@$A:$A)"
所以 @ 出现在不同的位置。我手动将公式“=@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 的单元格(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) 中的 cell(1,1) , rng2 的所有单元格怎么可能不是引用单元格(1,1)而是引用正确的单元格(单元格(2,1)、单元格(3,1)等)?
而且我也想明白为什么@在使用函数EXP时默认放在范围前面,而在使用我自己的函数时@默认放在函数名前面。有人有想法吗?
要求值的函数必须是字符串(双引号之间)...
请试试
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 = Evaluate("EXP(" & rng1.Address & ")")
将 return 值数组,但只会将第一个值输入到 rng2
中的每个单元格中。
所以,即使你可以模仿 EXP 正在做的事情,它仍然不会做你想要的。
但是,如果您仍想模仿使用 EXP
函数获得的(可能不正确的)功能:
EXP 和您的 UDF 之间的区别在于 EXP 函数可以处理和 return 数组,而您的 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 版本只接受一个单元格或值,而工作表版本能够接受 range/array 个值。
通常,您可以使用 Application.WorksheetFunction
访问工作表版本,但不幸的是,这似乎不适用于 EXP
。
在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 & ")")
我现在想以同样的方式处理我自己的功能。为了进行测试,我创建了一个函数,该函数 returns 输入的指数值:
Function TestExpo(alpha) As Double
TestExpo = EXP(alpha)
End Function
并且我按照之前使用 Evaluate() 所述的相同方式进行操作:
rng2 = Evaluate("TestExpo(" & rng1.Address & ")")
我没有收到错误,但 Excel sheet 上什么也没发生。我试图通过在函数名称中输入错误来触发错误,例如:
rng2 = Evaluate("TestEx(" & rng1.Address & ")")
我实际上在 sheet 上的 rng2 的每个单元格中都遇到了“名称”错误。因此,调用函数“TestExpo”起作用。我还尝试使用实数而不是 rng1.Address:
rng2 = Evaluate("TestExpo(" & 2 & ")")
并且我在 sheet 上的 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 sheet.
上也没有任何值我运行没主意了...我试图找到VBA的EXP()函数的代码,看看如何指定输入和输出,认为如果我在函数“TestExpo”中使用相同的类型和参数,它可能会工作,但我找不到 EXP() 函数的代码,它可能不是 public.
有没有人遇到过类似情况并解决了?
亲切的问候 泽维尔
更新:
感谢您的贡献!我继续前进!
当我使用时,正如@FaneDuru所建议的那样
rng2 = Evaluate("""=TestExpo(" & rng1.Address & ")""")
我在每个单元格中都遇到了值错误。检查工作中的单元格sheet,我可以看到每个单元格都归因于公式:
"=@TestExpo($A:$A)"
我比较写作
rng2 = Evaluate("""=EXP(" & rng1.Address & ")""")
哪个有效并将公式归因于每个单元格
"=EXP(@$A:$A)"
所以 @ 出现在不同的位置。我手动将公式“=@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 的单元格(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) 中的 cell(1,1) , rng2 的所有单元格怎么可能不是引用单元格(1,1)而是引用正确的单元格(单元格(2,1)、单元格(3,1)等)?
而且我也想明白为什么@在使用函数EXP时默认放在范围前面,而在使用我自己的函数时@默认放在函数名前面。有人有想法吗?
要求值的函数必须是字符串(双引号之间)...
请试试
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 = Evaluate("EXP(" & rng1.Address & ")")
将 return 值数组,但只会将第一个值输入到 rng2
中的每个单元格中。
所以,即使你可以模仿 EXP 正在做的事情,它仍然不会做你想要的。
但是,如果您仍想模仿使用 EXP
函数获得的(可能不正确的)功能:
EXP 和您的 UDF 之间的区别在于 EXP 函数可以处理和 return 数组,而您的 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 版本只接受一个单元格或值,而工作表版本能够接受 range/array 个值。
通常,您可以使用 Application.WorksheetFunction
访问工作表版本,但不幸的是,这似乎不适用于 EXP
。