I have been building a small library of VBA functions. I have a small section with higher order functions that I'd like to share. It uses strings as delegates/function pointers and Application.Run
to actually call the functions.
This isn't by any means a true implementation; just a quick and dirty implementation without connecting external libraries or creating objects for every function. I haven't found real drawbacks yet. Application.Run
always passes arguments by value, which would be a draw back, but is desirable in all of these implementations. Also not being able to declare lambdas is a buzz-kill.
Public Sub Map(ByVal delegate As String, ByRef sequence As Variant)
Dim i As Long
For i = LBound(sequence) To UBound(sequence)
sequence(i) = Application.Run(delegate, sequence(i))
Next i
End Sub
Public Function Mapped(ByVal delegate As String, ByVal sequence As Variant) As Variant
Mapped = sequence
Map delegate, Mapped
End Function
Public Function Fold(ByVal delegate As String, ByVal sequence As Variant, _
ByVal initial_value As Variant) As Variant
Fold = initial_value
Dim el As Variant
For Each el In sequence
Fold = Application.Run(delegate, Fold, el)
Next el
End Function
Public Function Reduce(ByVal delegate As String, ByVal sequence As Variant) As Variant
' Head and Tail return the first element and the rest, respectively.
Reduce = Fold(delegate, Tail(sequence), Head(sequence))
End Function
Public Function Compose(ByVal delegates As Variant, ByVal initial_value As Variant) As Variant
Compose = initial_value
Dim delegate As Variant
For Each delegate In delegates
Compose = Application.Run(delegate, Compose)
Next delegate
End Function
-
2\$\begingroup\$ When you say "without connecting external libraries" did you mean something like codeproject.com/Articles/204509/Functors-in-VBA \$\endgroup\$Snowbody– Snowbody2014年08月16日 06:38:12 +00:00Commented Aug 16, 2014 at 6:38
-
\$\begingroup\$ Yes. However that is a far better library than the one's I had had heard about \$\endgroup\$cheezsteak– cheezsteak2014年08月16日 15:11:50 +00:00Commented Aug 16, 2014 at 15:11
2 Answers 2
Things I like
- This idea.
- You've explicitly declared
ByVal
andByRef
. - All methods are short, clear, and concise.
Things to watch out for
- All of these functions take variants in as parameters, by necessity. Client code runs the risk of passing in a
sequence
that isn't enumerable. These really need a comment and maybe aVB_Description
attribute describing what kind of arguments the functions expect. - You also may want to consider handling runtime error 438 "Object doesn't support Method" for the functions that use
For Each
loops. There's not much you can do other than return a more meaningful error message though. - If you change the loop in
Map
to aFor Each
and manually incrementi
, all of the functions will be compatible with collections as well as arrays.
Nitpicks
- Underscores have a special meaning. They indicate event procedures. So I would change
initial_value
toinitialValue
. - While I quickly understood that
el
stood forelement
, there's no reason not to just spell it out.
-
\$\begingroup\$ If the loop in
Map
is changed to aFor Each
andi
is manually incremented, then one shoulddeclare Option Base 1
since Arrays are zero-indexed by default, while Collections are one-indexed. \$\endgroup\$klausnrooster– klausnrooster2024年02月19日 01:31:15 +00:00Commented Feb 19, 2024 at 1:31
you have extra newlines in your for loops, and other blocks of code, like this
For Each el In sequence Fold = Application.Run(delegate, Fold, el) Next el
Remove them
For Each el In sequence
Fold = Application.Run(delegate, Fold, el)
Next el
There isn't the need for them.
your functions and subs also look like this,
Public Function Mapped(ByVal delegate As String, ByVal sequence As Variant) As Variant Mapped = sequence Map delegate, Mapped End Function
it looks like you think something needs to be added but you didn't put in any comments to say what you are thinking about...
Public Function Mapped(ByVal delegate As String, ByVal sequence As Variant) As Variant
Mapped = sequence
Map delegate, Mapped
End Function
is what this should look like.
-
5\$\begingroup\$ Am I correct in assuming this is just a personal preference? I did remove blank lines until I was instructed to do differently. To be honest, I rather like using new lines to delineate different logical steps. \$\endgroup\$cheezsteak– cheezsteak2014年08月15日 18:08:59 +00:00Commented Aug 15, 2014 at 18:08
-
\$\begingroup\$ @ptwales, that was all in the same Sub, your subs and functions don't have that much in them that they need to be separated to distinguish between steps. I would space between the functions and subs though, that makes sense. \$\endgroup\$Malachi– Malachi2014年08月15日 18:21:40 +00:00Commented Aug 15, 2014 at 18:21
-
3\$\begingroup\$ I would definitely class it under personal preference. \$\endgroup\$Kaz– Kaz2015年12月18日 14:47:11 +00:00Commented Dec 18, 2015 at 14:47
Explore related questions
See similar questions with these tags.