-
Notifications
You must be signed in to change notification settings - Fork 61
stdLambda Class
A lambda expression/anonymous function is a function definition that is not bound to a name. Lambda expressions are usually "1st class citezens" which means they can be passed to other functions for evaluation.
I personally believe this is best described with an example. Imagine we wanted to sort an array of sheets by their name. In VBA this would be relatively complex and require an understanding of how to sort data in the first place, as well as which algorithms to use. Lambda allows us to define 1 sorting function and then provide our lambda function to provide the ID to sort on:
Sub Main
myArray = Array(Sheets(1),Sheets(2))
newArray = sort(myArray, stdLambda.Create("$1.name"))
End Sub
Function sort(array as variant, accessor as stdICallable)
'... sorting code ...
elementID = accessor(element)
'... sorting code ...
End Function
Currently the build process isn't particularly great. I would strongly suggest:
- Download a copy of the github repository
- Drag and Drop
stdICallable
andstdLambda
into the VBA modules pane
At a later date a better build process will be added to the VBA-STD-Library, to hopefully simplify this process.
Takes 2 parameters, the expression to evaluate as a string, and whether the function environment should be sandboxed. Sandboxed function environment might be useful in cases where you are using stdLambda
for both programming and user customisability, but don't want the user to have access to all data / functions you do.
Sub test()
Dim cb as stdLambda
set cb = stdLambda.Create("1+1")
End Sub
To define a function which takes multiple arguments $#
should be used where #
is the index of the argument. E.G. $1
is the first argument, $2
is the 2nd argument and $n is the nth argument.
Sub test()
Dim average as stdLambda
set average = stdLambda.Create("($1+$2)/2")
End Sub
You can also define functions which call members of objects. E.G.
Sub test()
Debug.Print stdLambda.Create("$1.Name")(ThisWorkbook) 'returns ThisWorkbook.Name
Call stdLambda.Create("$1#Save")(ThisWorkbook) 'calls ThisWorkbook.Save
End Sub
The lambda syntax comes with many VBA functions which you are already used to...
Sub test()
Debug.Print stdLambda.Create("Mid($1,1,5)")("hello world") 'returns "hello"
Debug.Print stdLambda.Create("$1 like ""hello*""")("hello world") 'returns true
End Sub
As well as an inline if statement (execution style subject to change):
Sub test()
Debug.Print stdLambda.Create("if $1 then 1 else 2")(true) 'returns 1
Debug.Print stdLambda.Create("if $1 then 1 else 2")(false) 'returns 2
End Sub
See current list of operators and functions
One of the main ways of executing code is using the default action. Here are a few examples:
Sub test()
Dim average as stdLambda
set average = stdLambda.Create("($1+$2)/2")
Debug.Print average(1,2) '1.5
End Sub
Run is the default action, so you can optionally use Run
directly:
Sub test()
Dim average as stdLambda
set average = stdLambda.Create("($1+$2)/2")
Debug.Print average.Run(1,2) '1.5
End Sub
RunEx
is useful in situations where you have an array of arguments, and you need to pass these to the lambda expression instead of ParamArray arguments:
Sub test()
Dim average as stdLambda
set average = stdLambda.Create("($1+$2)/2")
Dim args as variant
args = Array(1,2)
Debug.Print average.RunEx(args) '1.5
End Sub
The stdICallable
interface is a shared interface between stdLambda
and stdCallback
and can be used to execute these functions in code:
Sub test(ByVal func as stdICallable)
func.Run(ThisWorkbook, 1, "hello world")
End Sub
One of the core uses of stdLambda
is for use with stdArray
, an array class declared in src/stdArray.cls
. The following examples can be used to see the use of the stdLambda
object:
'Create an array
Dim arr as stdArray
set arr = stdArray.Create(1,2,3,4,5,6,7,8,9,10) 'Can also call CreateFromArray
'More advanced behaviour when including callbacks! And VBA Lamdas!!
Debug.Print arr.Map(stdLambda.Create("$1+1")).join '2,3,4,5,6,7,8,9,10,11
Debug.Print arr.Reduce(stdLambda.Create("$1+$2")) '55 ' I.E. Calculate the sum
Debug.Print arr.Reduce(stdLambda.Create("Max($1,$2)")) '10 ' I.E. Calculate the maximum
Debug.Print arr.Filter(stdLambda.Create("$1>=5")).join '5,6,7,8,9,10
'Execute property accessors with Lambda syntax
Debug.Print arr.Map(stdLambda.Create("ThisWorkbook.Sheets($1)")) _
.Map(stdLambda.Create("$1.Name")).join(",") 'Sheet1,Sheet2,Sheet3,...,Sheet10
'Execute methods with lambda:
Call stdArray.Create(Workbooks(1),Workbooks(2)).forEach(stdLambda.Create("$1#Save")
'We even have if statement!
With stdLambda.Create("if $1 then ""lisa"" else ""bart""")
Debug.Print .Run(true) 'lisa
Debug.Print .Run(false) 'bart
End With
stdLambda
provides you with the ability to extend syntax with custom functions and objects.
Let's say for example we have a Lookups
dictionary which we want to use to access some additional data to filter some codes.
Sub Main()
if stdLambda.oFunctExt is nothing then set stdLambda.oFunctExt = CreateObject("Scripting.Dictionary")
'Bind MyLookups to all unsandboxed lambdas
set stdLambda.oFunctExt("MyLookups") = Lookups
'Create and evaluate:
Debug.Print stdArray.Create("MBG","TBD", "CLR").map(stdLambda.Create("MyLookups($1)(""FullName"")")).join() '...
End Sub
As a pre-requisite I'd suggest you add stdCallback.cls
to your project as this will help wrap your functions in objects, required for binding to stdLambda
.
Sub Main()
if stdLambda.oFunctExt is nothing then set stdLambda.oFunctExt = CreateObject("Scripting.Dictionary")
'Bind function to
set stdLambda.oFunctExt("MyFunction") = stdCallback.CreateFromModule("ModuleMain","MyRealFunctionName")
'Execute!
Debug.Print stdLambda.Create("MyFunction()")() 'true
End Sub
Function MyRealFunctionName() as boolean
MyRealFunctionName = true
End Function