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