How to write one universal function to evaluate many equations?

I am working on verifying whether if equations are valid over a large data set. The challenge is that the number of equations to be verified is huge even though they are simple arithmetic equations and identities. In order to avoid writing a different function for each equation, I try to represent an equation as a 'model' and write only one universal function to eval each model. Here is a toy example to explain what I intend to do. My questions are 1.) whether if my approach is generally in right direction 2.) is there any way to avoid eval( ) in function foo( )? Thanks for your insights!
x = 7;
y = 3;
modelA = [x, y; "+", "-"];
foo(modelA)
ans = 4
z = -21;
modelB = [x, y, z; "-", "*", "=="];
foo(modelB)
ans = logical
1
function out = foo(model)
data = string(model(1,:));
operators = model(2,:);
a = strcat(operators, data);
out = eval(strcat(a{:}));
end

15 comentarios

are the variables strictly integers?
"In order to avoid writing a different function for each equation"
Why?
Simon
Simon el 2 de Jul. de 2023
Editada: Simon el 2 de Jul. de 2023
The real problem I work on is checking whether if numbers from many financial statements do satisfy accounting equations. For example, liability + equity == asset (or not). There are maybe over a hundred or more similar identities. And in other cases, I would like to get ratios. I could certainly write function for each accounting equation; as a matter of fact, I have done a couple of that. Then I found this task is too tedious. Othere disadvantages include 1.) function naming and tracking would be out of control and 2.) accounting equations and their purposes are hidden inside functions, which I feel uncomfortable about. Any suggestion?
Stephen23
Stephen23 el 2 de Jul. de 2023
Editada: Stephen23 el 2 de Jul. de 2023
"I could certainly write function for each accounting equation; as a matter of fact, I have done a couple of that. Then I found this task is too tedious."
I don‘t see that it makes any difference: if you can define those string arrays then you can just as easily define some anonymous functions e.g. using STR2FUNC.
"Othere disadvantages include 1.) function naming and tracking would be out of control"
The approach you show in your question also requires lots of individually-named functions. You could easily avoid that with a cell array of anonymous functions or function handles. Or just one function with suitable SWITCH or other logical operations to perform the calculations. Or some suitable nesting of local functions, called based on some logical criteria.
"2.) accounting equations and their purposes are hidden inside functions, which I feel uncomfortable about."
Those are benefits: Mfile functions allow you to neatly and efficiently encapsulate some functionality and provide a documented interface, as well as limitless help, comments, etc. Calling functions is also very efficient. Your string arrays provide none of those benefits.
@Stephen23 The cell array of anonymous functions seem to be a great idea. I will try it out. Thanks!
A quck array of anonymous function is scribbled. It looks neat and managable.
C = ["@(x, y, z) x-y==z"; "@(x, y) x/y"]
C = 2×1 string array
"@(x, y, z) x-y==z" "@(x, y) x/y"
foo = str2func(C(1));
foo(3,2,1)
ans = logical
1
goo = str2func(C(2));
goo(4, 5)
ans = 0.8000
John D'Errico
John D'Errico el 2 de Jul. de 2023
Editada: John D'Errico el 2 de Jul. de 2023
You are going to have many more problems. That is, now you are talking about ratios, but then you are testing for exact equality. That is a bad idea, as you should never tyest for exact equality of floating point numbers. Those tests will unpredictably fail, and then you will be left scratching your head, eventually coming back here to understand the concept I just mentioned. NEVER TEST FOR EXACT EQUALITY BETWEEN FLOATING POINT NUMBERS.
I'd suggest you start rethinking what you need to do, and find a better way than using eval anyway. Eval is slow as hell. It will surely cause you problems down the line too.
For example, you might decide to reduce this to a set of generic expresssions. Now, write one function for each of those generic classes. Pass in your variables, get a result. In the case where a ratio is involved, then you will probably need to apply a tolerance on the result.
How many variables do these formulas refer to? If there are many, it might be easier to pass them in a scalar structure and let each function decide for itself which field’s it wants to use.
@Stephen23 'How many variables do these formulas refer to?' It ranges from 3 to 7. In the simplest one, L+E=A, there are three; in more fine-grained equations for sub-factors of, say 'current asset == cash equvalents + notes + inventories + ...' and 'liabilities', there could be more than 5 variables. I couldn't use the number of variables to discern equations.
Because accounting equations have hirachical structure, I hope my codes could adapto that. Like this equation Assets = Capital introduced + (Income – Expenses) – Drawings + Liabilities; or (Income - Expenses) could be replaced by a higher variable. This is one of the reasons I try to find an alternative approach, which would by-pass writing a function for each equation.
Simon
Simon el 2 de Jul. de 2023
Editada: Simon el 2 de Jul. de 2023
@John D'Errico Thanks for the feedback. 'NEVER TEST FOR EXACT EQUALITY BETWEEN FLOATING POINT NUMBERS.'
I am aware of that potential pitfall. Fortunately, only integer variables will be check for exact equality. The double-valued ratios will just be output of the universalFunction(data, equation script).
'I'd suggest you start rethinking what you need to do, and find a better way than using eval anyway. Eval is slow as hell. It will surely cause you problems down the line too.'
I totally agree. I have read @Stephen23's tutorial post for Matlab beginner. It has in-depth discussion about eval( ). I have indeed been thinking really hard about this problem. I could have taken a more conventional approach, writing many functions. But trying a very different way to tackle this problem is very seductive :-)
'For example, you might decide to reduce this to a set of generic expresssions. Now, write one function for each of those generic classes. Pass in your variables, get a result.'
Good advice. think I can divide them into two types: logical equality comparison and numeric calculation.
'In the case where a ratio is involved, then you will probably need to apply a tolerance on the result.'
Good advice.
What's the advantage of entering everything as a string and then using str2func, as opposed to storing the functions directly in a cell array?
C = ["@(x, y, z) x-y==z"; "@(x, y) x/y"]
C = 2×1 string array
"@(x, y, z) x-y==z" "@(x, y) x/y"
foo = str2func(C(1));
foo(3,2,1)
ans = logical
1
goo = str2func(C(2));
goo(4, 5)
ans = 0.8000
clear
C = {@(x,y,z) x - y == z; @(x,y) x/y;}
C = 2×1 cell array
{@(x,y,z)x-y==z} { @(x,y)x/y}
C{1}(3,2,1)
ans = logical
1
C{2}(4,5)
ans = 0.8000
Or, use a struct and then name the functions for clarity?
clear
C.foo = @(x,y,z) x - y == z;
C.goo = @(x,y) x/y;
C.foo(3,2,1)
ans = logical
1
C.goo(4,5)
ans = 0.8000
Simon
Simon el 2 de Jul. de 2023
Editada: Simon el 2 de Jul. de 2023
@Paul 'What's the advantage of entering everything as a string and then using str2func, as opposed to storing the functions directly in a cell array?'
I was trying out solutions generously suggested to me here. One advantage I could think of is to turn it into a table so that I can store 'cooments' in a new column.
'Or, use a struct and then name the functions for clarity?'
Naming function or variable is the biggest thing that slow my coding down significantly. I have an uncontrolable habit that I change names spontaneously.
I didn't know structure can be used in such a versatile way. It's wonderful to have learned that. Thanks!
"What's the advantage of entering everything as a string and then using str2func, as opposed to storing the functions directly in a cell array?"
Advantage: the "equations" can be dynamically defined/created, but still offer the efficiency of calling function handles.
If the "equations" do not need to be dynamically created then simple (anonymous) function handles would be better.
Comments can be stored using either the cell array or struct, if a cell array or struct with named fields happen to be the way to go.
C{1,1} = @(x,y,z) x - y == z;
C{1,2} = "this is the foo function";
C
C = 1×2 cell array
{@(x,y,z)x-y==z} {["this is the foo function"]}
% or
S.foo.eqn = @(x,y,z) x - y == z;
S.foo.comment = "this is the foo function";
S.foo
ans = struct with fields:
eqn: @(x,y,z)x-y==z comment: "this is the foo function"
Simon
Simon el 4 de Jul. de 2023
Editada: Simon el 4 de Jul. de 2023
@Paul I tried out your idea (structure of function handels). It works pretty well. Thanks!!

Iniciar sesión para comentar.

Respuestas (0)

Categorías

Más información sobre Variables en Centro de ayuda y File Exchange.

Productos

Versión

R2023a

Preguntada:

el 2 de Jul. de 2023

Editada:

el 4 de Jul. de 2023

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by