What is the great wisdom in defining your own object if I can't do this? - excel

This is for Excel and VBA. Assume that BondClass has been properly defined in a Class Module. I get a #VALUE! when I type "=GetBondPrincipal()" into an Excel cell. Have I done something syntactically wrong or is this just not possible in Excel/VBA? I ask because what I really want to do is this:
Return a User Defined Data Type in an Excel Cell
but am unable to find a solution. So at the very least, I want to know if what I want to do below is possible.
Option Explicit
Function InitializeBond(ir As Double, p As Double) As BondClass
Dim mybond As BondClass
Set mybond = New BondClass
Call mybond.Initialize(ir, p)
InitializeBond = mybond
End Function
Function GetBondPrincipal()
Dim b As BondClass
Set b = New BondClass
b = InitializeBond(0.03, 100) //the code quits here,
//it doesn't like the BondClass return type?
GetBondPrincipal = b.GetPrincipal()
End Function
I know that in the example I have provided, I don't have to call InitializeBond and can simply type "Call b.Initialize(.03,100)". The code will work fine if I do this. But I can't seem to be able to get a UDF to return a type other than the built-in types. Any way to do any of this? Do I have to define assignment for non-built-in types?

Hint: use "SET".
(most common error made by VB programmers in VB6 and VBA).

Anyway, I don't find that very elegant.
I would rather write:
Function GetBondPrincipal()
Dim b As BondClass
Set b = New BondClass
with b
.param1 = 0.03
.param2 = 100
.InitializeBond
GetBondPrincipal = .GetPrincipal()
end with
End Function

Related

VBA reading global from cell

I am a complete VBA newbie, so apologies in advance for the trivial question. Consider the following code:
Dim frog As Double
frog = Range("A1").Value
Function test_func(ByVal a As Double, ByVal b As Double)
test_func = a ^ b
End Function
Private Sub btnAddNumbersFunction_Click()
MsgBox test_func(frog, 3)
End Sub
When I try to compile this, I get an error of "invalid outside procedure" with the "A1" highlighted. (I am trying to define a model with some user-settable parameters, and so this would be useful to have).
This code sample will compile, I have moved the variable assignment into the sub:
Dim frog As Double
Function test_func(ByVal a As Double, ByVal b As Double)
test_func = a ^ b
End Function
Private Sub btnAddNumbersFunction_Click()
'Assign values inside subs or functions
frog = Range("A1").Value
MsgBox test_func(frog, 3)
End Sub
It seems that introducing another "initialization" sub, where the globals are initialized, does the trick. The suggestions in the other answers are not satisfactory for the reasons explained in the question: having to initialize (or even mention) the parameters in every procedure that implicitly uses them defeats the purpose. I am a bit mystified about why this is the design (this is unlike any other language I know), but MSFT moves in mysterious ways its wonders to perform.

Why declare function's type?

Sorry if this has been asked, but seriously can't find anything, so would also appreciate on how to search for this stuff.
So my question: what is the point of declaring the function's type in general? E.g. here 'as double'
Function myFunction(ByVal j As Integer) As Double
Return 3.87 * j
End Function
For a normal variable it has tons of benefits, like less memory, easier to see typos, but why here?
Edit: so, it's good because we can avoid errors, like it giving back a different type of values than expected.
Functions RETURN something. That type is the type of the return.
In your function:
Function myFunction(ByVal j As Integer) As Double
Return 3.87 * j
End Function
You are returning a decimal, so type Double make sense.
If you don't return anything, then you can declare it as a Sub.
And, for clarification, your function would throw a compile error. Unlike other languages, in VBA to return, we set the function name's value to the thing we want to return:
Function myFunction(ByVal j As Integer) As Double
myFunction=3.87 * j
End Function
Now we can call this function to get the Double value that it creates:
Sub testSub()
msgbox("This is the result of the function: " & myFunction(10))
End Sub
Which would launch a message box saying "This is the result of the function: 38.7"
Since I can't mark a comment the answer, let me quote:
#John Coleman
My opinion is that it a good thing to declare your return types because it increases the likelihood that the compiler will complain when you are doing something that really doesn't make sense.
Excel VBA is different from other programming languages in that it centers around a particular application: Excel.
Functions are useful in Excel VBA primarily because they can be typed directly into a cell on a sheet by an end user. User defined functions provide near infinite flexibility. The value the user defined function prints to Excel is formatted based on the function's type--and in a program which is about data visualization, formatting is a huge part.
For example, try putting these four functions into a blank worksheet module:
Function myInt(x, y) As Integer
myInt = x / y
End Function
Function myDouble(x, y) As Double
myDouble = x / y
End Function
Function myString(x, y) As String
myString = x / y
End Function
Function myVariant(x, y)
myVariant = x / y
End Function
Next, enter each of these functions into a different cell in the workbook. Use x=1 and y=2.
myInt produces "=0"
myDouble produces "=0.5"
myString produces "'0"
myVariant produces "=0.5"
If you're okay with Excel deciding how to format your result, that's your choice, but specifying the type offers an entire new level of control. For example, by simply declaring a function an integer, you can avoid having to devote a line of code to rounding. By declaring a function to be a string, you can avoid several lines of formatting code trying to get a number to be saved as text instead.

How to evaluate an arbitrary mathematical function the user inputs in a visual basic 6 exe?

I have made a program that plots a slope field for a given function.
It works correct if I plug in the function in the source code.
But I wan to turn this vb6 project into an exe file.
Because I knew this will happen before, I previously made a field for inputting the function. The function will be inputted in a special form. inspired by the language used in some add on in AutoCAD, I made this language and named it DiffSol.
So what the user is gonna do is to write a function in the field using DiffSol language.
The problem is that It needs to be a real mathematical function in vb to be evaluated for different x and y's. but I cannot find a strategy to turn that language into a vb math function which can be evaluated.
All I am gonna do is to evaluate the inputted function for 15*31 times.
The job looks like making a compiler. It seems a really hard job for me.
Any ideas?
Simple way is to transpile it in to VBScript or JScript.
Create a class containing your functions written in VB:
' +(a,b)
public function ADD(a as double, b as double) as double
add = a + b
end function
'/(a,b)
public function DIV(a as double, b as double) as double
div = a / b
end function
Add a reference to the Microsoft Script Control then:
Dim scr As ScriptControl: Set scr = New ScriptControl
scr.Language = "VBScript"
'// allow the script access to the class with the functions
scr.AddObject "DS", new diffsolClass
expr = " +(200, c(/(+(2,6), 2))) "
'//parse with simple substitution
parsed = expr
parsed = Replace$(parsed, "/", "DS.DIV")
parsed = Replace$(parsed, "+", "DS.ADD")
parsed = Replace$(parsed, "c", "cos") '//built in already
'//for some valid VB: DS.ADD(200, cos(DS.DIV(DS.ADD(2,6), 2)))
'//run it
MsgBox scr.Eval(parsed)
returns 199.346356379136

Excel VBA failure of repeated Evaluate method

I have written a little tool in VBA that charts a function you pass it as a string (e.g. "1/(1+x)" or "exp(-x^2)"). I use the built-in Evaluate method to parse the formula. The nub of it is this function, which evaluates a function of some variable at a given value:
Function eval(func As String, variable As String, value As Double) As Double
eval = Evaluate(Replace(func, variable, value))
End Function
This works fine, e.g. eval("x^2, "x", 2) = 4. I apply it element-wise down an array of x values to generate the graph of the function.
Now I want to enable my tool to chart the definite integral of a function. I have created an integrate function which takes an input formula string and uses Evaluate to evaluate it at various points and approximate the integral. My actual integrate function uses the trapezoidal rule, but for simplicity's sake let's suppose it is this:
Function integrate(func As String, variable As String, value As Double) As Double
integrate = value * (eval(func, variable, 0) + eval(func, variable, value)) / 2
End Function
This also works as expected, e.g. integrate("t", "t", 2) = 2 for the area of the triangle under the identity function.
The problem arises when I try to run integrate through the charting routine. When VBA encounters a line like this
eval("integrate(""t"",""t"",x)", "x", 2)
then it will stop with no error warning when Evaluate is called inside the eval function. (The internal quotes have to be doubled up to read the formula properly.) I expect to get the value 2 since Evaluate appears to try and evaluate integrate("t", "t", 2)
I suspect the problem is with second call on Evaluate inside integrate, but I've been going round in circles trying to figure it out. I know Evaluate is finicky and poorly documented http://fastexcel.wordpress.com/2011/11/02/evaluate-functions-and-formulas-fun-how-to-make-excels-evaluate-method-twice-as-fast but can anyone think of a way round this?
Thanks
George
Excel 2010 V14, VBA 7.0
Thanks Chris, your Debug.Print suggestion got me thinking and I narrowed the problem down a bit more. It does seem like Evaluate gets called twice, as this example shows:
Function g() As Variant
Debug.Print "g"
g = 1
End Function
Run from the Immediate Window:
?Evaluate("g()")
g
g
1
I found this http://www.decisionmodels.com/calcsecretsh.htm which shows a way round this by using Worksheet.Evaluate (Evaluate is actually the default for Application.Evaluate):
?ActiveSheet.Evaluate("g()+0")
g
1
However this still doesn't solve the problem with Evaluate calling itself. Define
Function f() As Variant
Debug.Print "f"
f = ActiveSheet.Evaluate("g()+0")
End Function
Then in the Immediate Window:
?ActiveSheet.Evaluate("f()+0")
f
Error 2015
The solution I found was define a different function for the second formula evaluation:
Function eval2(formula As String) As Variant
[A1] = "=" & formula
eval2 = [A1]
End Function
This still uses Excel's internal evaluation mechanism, but via a worksheet cell calculation. Then I get what I want:
?eval2("f()")
f
g
1
It's slower due to the repeated worksheet hits, but that's the best I can do. So in my original example, I use eval to calculate the integral and eval2 to chart it. Still interested if anyone has any other suggestions.

Function Overloading and UDF in Excel VBA

I'm using Excel VBA to a write a UDF. I would like to overload my own UDF with a couple of different versions so that different arguments will call different functions.
As VBA doesn't seem to support this, could anyone suggest a good, non-messy way of achieving the same goal? Should I be using Optional arguments or is there a better way?
Declare your arguments as Optional Variants, then you can test to see if they're missing using IsMissing() or check their type using TypeName(), as shown in the following example:
Public Function Foo(Optional v As Variant) As Variant
If IsMissing(v) Then
Foo = "Missing argument"
ElseIf TypeName(v) = "String" Then
Foo = v & " plus one"
Else
Foo = v + 1
End If
End Function
This can be called from a worksheet as =FOO(), =FOO(number), or =FOO("string").
If you can distinguish by parameter count, then something like this would work:
Public Function Morph(ParamArray Args())
Select Case UBound(Args)
Case -1 '' nothing supplied
Morph = Morph_NoParams()
Case 0
Morph = Morph_One_Param(Args(0))
Case 1
Morph = Two_Param_Morph(Args(0), Args(1))
Case Else
Morph = CVErr(xlErrRef)
End Select
End Function
Private Function Morph_NoParams()
Morph_NoParams = "I'm parameterless"
End Function
Private Function Morph_One_Param(arg)
Morph_One_Param = "I has a parameter, it's " & arg
End Function
Private Function Two_Param_Morph(arg0, arg1)
Two_Param_Morph = "I is in 2-params and they is " & arg0 & "," & arg1
End Function
If the only way to distinguish the function is by types, then you're effectively going to have to do what C++ and other languages with overridden functions do, which is to call by signature. I'd suggest making the call look something like this:
Public Function MorphBySig(ParamArray args())
Dim sig As String
Dim idx As Long
Dim MorphInstance As MorphClass
For idx = LBound(args) To UBound(args)
sig = sig & TypeName(args(idx))
Next
Set MorphInstance = New MorphClass
MorphBySig = CallByName(MorphInstance, "Morph_" & sig, VbMethod, args)
End Function
and creating a class with a number of methods that match the signatures you expect. You'll probably need some error-handling though, and be warned that the types that are recognizable are limited: dates are TypeName Double, for example.
VBA is messy. I'm not sure there is an easy way to do fake overloads:
In the past I've either used lots of Optionals, or used varied functions. For instance
Foo_DescriptiveName1()
Foo_DescriptiveName2()
I'd say go with Optional arguments that have sensible defaults unless the argument list is going to get stupid, then create separate functions to call for your cases.
You mighta also want to consider using a variant data type for your arguments list and then figure out what's what type using the TypeOf statement, and then call the appropriate functions when you figure out what's what...

Resources