Reputation: 721
How can I create a dynamic operator using VBA?
swt = True
op = IIf(swt = True, "<", ">")
a = 10
B = 20
IF a op B then
MsgBox ("a is greater than B")
End If
Obviously this fails but can anyone make it work?
Upvotes: 5
Views: 3975
Reputation: 52008
As I said in the comments and others have mentioned as well, VBA syntax doesn't allow you to define new infix operators.
You never said what you wanted to do this for, but I couldn't help but think that you had a custom sort in mind where a comparison operator is passed to a sort sub. Think about how such things are handled in C (which also doesn't allow you to define new infix operators). The standard library has an implementation of qsort
which needs to be passed a pointer to a comparison function. You can do something similar in VBA.
VBA lacks function pointers -- but Application.Run
(which is in some ways more flexible than Application.Evaluate
) allows you to use the name of the function almost as if it is a pointer. For example, say you write a compare function that takes to doubles and either returns the larger of them or the smaller of them, depending on the state of a global parameter, like thus:
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
To illustrate how Application.Run
allows you to pass a custom compare function, I wrote a function which takes the name of a compare function as well as an array, and returns the element of the array which is largest according to the sort order determined by the compare function. The compare function can be rather arbitrary, e.g. it could return the tree of greatest height in an array of trees:
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
The following sub illustrates how this is called:
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
Upvotes: 1
Reputation: 71217
You're thinking it wrong: the VBA language grammar doesn't work like that, operators are well defined, and when this line gets tokenized:
If a op B Then
Seeing the If
keyword it will expect this:
If [BoolExp] Then
...and then bump into a op B
and throw a fit, because a
is an identifier, op
is an identifier, and B
is another identifier - there's no logical operator there, and that can't be evaluated as a [BoolExp]
- hence the compile error.
But you know this already.
swt = True op = IIf(swt = True, "<", ">")
IIf
works similarly: IIf([BoolExp], [ValueStmt], [ValueStmt])
- here swt
being assigned to the Boolean literal True
, it constitutes a Boolean expression all by itself. Thus, the assignment for op
can be simplified to this:
op = IIf(swt, "<", ">")
Now that's prettier, but op
is still a String
variable, and that just won't work.
Short of making Excel do the work with Application.Evaluate
, the only way is to branch in VBA code that works in all Office hosts is, well, to branch:
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
The edge case where a = b
also needs to be handled, of course.
Upvotes: 3
Reputation: 17637
Use the Evaluate()
method.
swt = True
op = IIf(swt = True, "<", ">")
a = 10
B = 20
If Evaluate(a & op & B) Then
MsgBox ("a is greater than B")
End If
Upvotes: 2
Reputation:
Use basic string concatenation methods to build a simple math formula bringing the operator(s) in as a string character. Once the formula is constructed from string parts, it can be resolved with 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
Upvotes: 5