VBA 中的动态运算符。我怎么能够?
Dynamic operator in VBA. How can I?
如何使用 VBA 创建动态运算符?
swt = True
op = IIf(swt = True, "<", ">")
a = 10
B = 20
IF a op B then
MsgBox ("a is greater than B")
End If
显然这失败了,但任何人都可以让它工作吗?
使用基本的字符串连接方法构建一个简单的数学公式,将运算符作为字符串字符输入。一旦公式由字符串部分构成,就可以用 Application Evaluate.
解析
Dim swt As Boolean, op As String
Dim a As Long, b As Long
swt = False
op = IIf(swt, "<", ">")
a = 10
b = 20
If Application.Evaluate(a & op & b) Then
MsgBox ("a is " & IIf(swt, "less", "greater") & " than b")
Else
MsgBox ("a is " & IIf(swt, "greater than or equal to", "less than or equal to") & " b")
End If
使用Evaluate()
方法。
swt = True
op = IIf(swt = True, "<", ">")
a = 10
B = 20
If Evaluate(a & op & B) Then
MsgBox ("a is greater than B")
End If
你想错了:VBA 语言语法不是那样工作的,运算符定义明确,当这一行被标记化时:
If a op B Then
看到 If
关键字,它会这样:
If [BoolExp] Then
...然后撞到a op B
就扔了一个,因为a
是一个标识符,op
是一个标识符,B
是另一个标识符- 那里没有逻辑运算符,不能评估为 [BoolExp]
- 因此编译错误。
但你已经知道了。
swt = True
op = IIf(swt = True, "<", ">")
IIf
的工作方式类似:IIf([BoolExp], [ValueStmt], [ValueStmt])
- 这里 swt
被分配给布尔文字 True
, 它本身构成了一个布尔表达式。因此,op
的赋值可以简化为:
op = IIf(swt, "<", ">")
现在更漂亮了,但是 op
仍然是一个 String
变量,那是行不通的。
缺少 ,唯一的方法是在 VBA 代码中分支,在所有 Office 主机中工作的代码是 分支:
If swt Then
If a < b Then msg = "a is smaller than b"
Else
if a > b then msg = "a is greater than b"
End If
MsgBox msg
当然,a = b
的边缘情况也需要处理。
正如我在评论中所说的和其他人也提到的,VBA 语法不允许您定义新的中缀运算符。
您从未说过要这样做的目的,但我不禁想到您有一个自定义排序,其中将比较运算符传递给排序子。想想在 C 中如何处理这些事情(它也不允许您定义新的中缀运算符)。标准库有一个 qsort
的实现,需要传递一个指向比较函数的指针。您可以在VBA中做类似的事情。
VBA 缺少函数指针——但是 Application.Run
(在某些方面比 Application.Evaluate
更灵活)允许您使用函数的名称,就好像它是一个指针。例如,假设您编写了一个比较函数,它采用双精度值,并且 return 是它们中较大的或较小的,具体取决于全局参数的状态,如下所示:
Public AZ As Boolean 'a global sort-order flag
Function compare(x As Double, y As Double) As Boolean
compare = IIf(AZ, (x < y), (y < x))
End Function
为了说明 Application.Run
如何允许您传递自定义比较函数,我编写了一个函数,它采用比较函数的名称以及数组,returns 的元素根据比较函数确定的排序顺序最大的数组。比较函数可以是任意的,例如它可以 return 树数组中最高的树:
Function Most(comp As String, items As Variant) As Variant
Dim i As Long, candidate As Variant, item As Variant
Dim lb As Long, ub As Long
lb = LBound(items)
ub = UBound(items)
candidate = items(lb)
For i = lb + 1 To ub
item = items(i)
If Application.Run(comp, candidate, item) Then
candidate = item
End If
Next i
Most = candidate
End Function
下面的子说明了这是如何调用的:
Sub test()
Dim A As Variant
A = Array(3, 6, 2, 8, 11, 15, 1, 10)
AZ = True
Debug.Print Most("compare", A) 'prints 15
AZ = False
Debug.Print Most("compare", A) 'prints 1
End Sub
如何使用 VBA 创建动态运算符?
swt = True
op = IIf(swt = True, "<", ">")
a = 10
B = 20
IF a op B then
MsgBox ("a is greater than B")
End If
显然这失败了,但任何人都可以让它工作吗?
使用基本的字符串连接方法构建一个简单的数学公式,将运算符作为字符串字符输入。一旦公式由字符串部分构成,就可以用 Application Evaluate.
解析Dim swt As Boolean, op As String
Dim a As Long, b As Long
swt = False
op = IIf(swt, "<", ">")
a = 10
b = 20
If Application.Evaluate(a & op & b) Then
MsgBox ("a is " & IIf(swt, "less", "greater") & " than b")
Else
MsgBox ("a is " & IIf(swt, "greater than or equal to", "less than or equal to") & " b")
End If
使用Evaluate()
方法。
swt = True
op = IIf(swt = True, "<", ">")
a = 10
B = 20
If Evaluate(a & op & B) Then
MsgBox ("a is greater than B")
End If
你想错了:VBA 语言语法不是那样工作的,运算符定义明确,当这一行被标记化时:
If a op B Then
看到 If
关键字,它会这样:
If [BoolExp] Then
...然后撞到a op B
就扔了一个,因为a
是一个标识符,op
是一个标识符,B
是另一个标识符- 那里没有逻辑运算符,不能评估为 [BoolExp]
- 因此编译错误。
但你已经知道了。
swt = True op = IIf(swt = True, "<", ">")
IIf
的工作方式类似:IIf([BoolExp], [ValueStmt], [ValueStmt])
- 这里 swt
被分配给布尔文字 True
, 它本身构成了一个布尔表达式。因此,op
的赋值可以简化为:
op = IIf(swt, "<", ">")
现在更漂亮了,但是 op
仍然是一个 String
变量,那是行不通的。
缺少
If swt Then
If a < b Then msg = "a is smaller than b"
Else
if a > b then msg = "a is greater than b"
End If
MsgBox msg
当然,a = b
的边缘情况也需要处理。
正如我在评论中所说的和其他人也提到的,VBA 语法不允许您定义新的中缀运算符。
您从未说过要这样做的目的,但我不禁想到您有一个自定义排序,其中将比较运算符传递给排序子。想想在 C 中如何处理这些事情(它也不允许您定义新的中缀运算符)。标准库有一个 qsort
的实现,需要传递一个指向比较函数的指针。您可以在VBA中做类似的事情。
VBA 缺少函数指针——但是 Application.Run
(在某些方面比 Application.Evaluate
更灵活)允许您使用函数的名称,就好像它是一个指针。例如,假设您编写了一个比较函数,它采用双精度值,并且 return 是它们中较大的或较小的,具体取决于全局参数的状态,如下所示:
Public AZ As Boolean 'a global sort-order flag
Function compare(x As Double, y As Double) As Boolean
compare = IIf(AZ, (x < y), (y < x))
End Function
为了说明 Application.Run
如何允许您传递自定义比较函数,我编写了一个函数,它采用比较函数的名称以及数组,returns 的元素根据比较函数确定的排序顺序最大的数组。比较函数可以是任意的,例如它可以 return 树数组中最高的树:
Function Most(comp As String, items As Variant) As Variant
Dim i As Long, candidate As Variant, item As Variant
Dim lb As Long, ub As Long
lb = LBound(items)
ub = UBound(items)
candidate = items(lb)
For i = lb + 1 To ub
item = items(i)
If Application.Run(comp, candidate, item) Then
candidate = item
End If
Next i
Most = candidate
End Function
下面的子说明了这是如何调用的:
Sub test()
Dim A As Variant
A = Array(3, 6, 2, 8, 11, 15, 1, 10)
AZ = True
Debug.Print Most("compare", A) 'prints 15
AZ = False
Debug.Print Most("compare", A) 'prints 1
End Sub