Updating Structure Input for Functions

Hello,
Let's say I have a structure for fruit:
fruit.appleMass = 0.15; %kg
fruit.appleHeight = 4; %m
Let's say I also have another structure for other variables:
otherParams.gravity = 9.81; %m/s^2
otherParams.newtonHeight = 1.68; %m
I have a simple function to calculate the force of a falling fruit and another simple function to calculate the maximum velocity of a falling fruit based on the apocryphal Isaac Newton tale:
ouch = fruitForce(fruit, otherParams);
yikes = fruitVelocity(fruit, otherParams);
function [ouch] = fruitForce(fruit, otherParams)
ouch = fruit.appleMass*otherParams.gravity;
end
function [yikes] = fruitVelocity(fruit, otherParams)
deltaHeight = fruit.appleHeight - otherParams.newtonHeight;
yikes = sqrt(2*deltaHeight*otherParams.gravity);
end
However, if later I discover I need to add another fruit to my structure, like a coconut, and need to perform the same calculations (poor Newton), I need to update the code.
Creating fields for coconuts is straightforward. Obviously, I could rewrite my functions to replace instances of apples with coconuts or add lines to calculate both apples and coconuts simultaneously. However, updating my functions each time I have a new fruit seems very ineffcient and seems highly prone to issues if I later have several function files calling fields from my fruit structure.
I think there is an easy way to update the code for an arbitrary number of fruits but not sure what that entails. What would be the best way to update and future proof the code? Should I have written the initial code differently before knowing there would be other fruits besides apples?
I have searched for similar questions and answers but didn't see anything similar (not sure what keyword I am missing). Insight would be much appreciated.

3 Kommentare

Stephen23
Stephen23 am 8 Jan. 2024
Bearbeitet: Stephen23 am 8 Jan. 2024
"Obviously, I could rewrite my functions to replace instances of apples with coconuts or add lines to calculate both apples and coconuts simultaneously."
Both of those options are best avoided.
"However, updating my functions each time I have a new fruit seems very ineffcient and seems highly prone to issues if I later have several function files calling fields from my fruit structure."
Yep, so don't do that.
"I think there is an easy way to update the code for an arbitrary number of fruits but not sure what that entails."
Hint: you are using a language which is based around arrays and indexing...
"What would be the best way to update and future proof the code?"
Use arrays.
"Should I have written the initial code differently before knowing there would be other fruits besides apples?"
Yes.
As soon as you force meta-data into variable or fieldnames like that then you make "future-proofing" your code much harder. Easily avoided by using arrays, matrices, and vectors.
"I have searched for similar questions and answers but didn't see anything similar (not sure what keyword I am missing)."
Indexing.
"Insight would be much appreciated."
Use arrays.
You are overthinking this.
Jekazu
Jekazu am 8 Jan. 2024
Hello Stephen,
Well, I admit I feel rather silly for missing something as simple as nonscalar structure arrays. I appreciate that you addressed many of the points in my inquiry.
Stephen23
Stephen23 am 8 Jan. 2024
Bearbeitet: Stephen23 am 9 Jan. 2024
Don't feel silly. I am just trying to show that a change of perspective helps: MATLAB is based on arrays, so you should always try to solve tasks first using arrays... and only if that does not work (unlikely) try something else.
This applies to all programming languages of course: if language X is based on Y... then it makes sense to use Y when writing code in X (at least as a starting point).
"I appreciate that you addressed many of the points in my inquiry."
Let me know what I missed, I am happy to clarify or provide examples.

Melden Sie sich an, um zu kommentieren.

 Akzeptierte Antwort

Stephen23
Stephen23 am 8 Jan. 2024
Bearbeitet: Stephen23 am 9 Jan. 2024

3 Stimmen

Do not use nested structures.
Definitely do NOT use lots of variables each named after a fruit! Ugh, no.
Use a structure array:
S(1).name = 'apple'
S(1).mass = 0.15; %kg
S(1).height = 4; %m
S(2).name = 'banana'
S(2).mass = 0.1; %kg
S(2).height = 2; %m
Do not make your data any more complex than that.
Do not force meta-data (e.g. names of fruit) into fieldnames or variable names (unless you want to force yourself into writing slow, complex, inefficient, buggy code that is hard to debug). Meta-data is data: data belongs in your variables, not in the variable/field names. Data in variables makes it much easier to write expandable, generalizable, efficient code. See also Steven Lord's comment here: https://www.mathworks.com/matlabcentral/answers/2067801-updating-structure-input-for-functions#comment_3022366
Understand that meta-data is data. Then you would avoid the situation that you have gotten yourself into.

5 Kommentare

Hello Stephen,
Now that I am aware of the point about using a structure array for fruit, should I use a different array for the structure "otherParams" or should everything be in one structure? As I see it, the two different structures have distinct types of data so intuitively my brain thinks there should be different variables to represent both sets of data. But per other comments, now I am trying to get into the mindset of keeping metadata out of my variable names and want to incorporate best practices before I potentially have many functions calling for this nonscalar structure array.
Should I then write functions like:
function [FMM] = fruitMassMultiplied(S)
FMM(1) = S(1)*4
end
Essentially, use the function name as metadata? Should I return S as the output with an extra field instead of a new structure FMM?
Hopefully that makes sense. If this should be a different post, my apologies, I will create one if needed.
Stephen23
Stephen23 am 9 Jan. 2024
Bearbeitet: Stephen23 am 9 Jan. 2024
"should I use a different array for the structure "otherParams" or should everything be in one structure?"
Things like gravitational acceleration or Newton's height do not depend on the fruit type, so have no business being stored in your fruit structure array.
"Should I then write functions like... Essentially, use the function name as metadata?"
I do not understand the question or the example.
"Should I return S as the output with an extra field instead of a new structure FMM?"
You could. Or you could write functions that just like you were doing before, only without specific fruit:
function ouch = fruitForce(fruit, otherParams)
ouch = fruit.mass*otherParams.gravity;
end
Unit-testing is probably easier with the simple output value.
Jekazu
Jekazu am 9 Jan. 2024
Hello Stephen,
Sorry that my example and question was not clear. You managed to answer the part about returning S as the output vs. a new structure (FMM in my example).
Based on other comments, it seems that the term "fruit" could be considered metadata. The intent of the function name question was to ask whether or not function names should be as generic as possible or as specific as desired (including metadata like "fruit" in the name). I think for the falling fruit problem, a very generic function name could be "fallingObjectForce" compared to "fruitForce". But I suspect that approaches programmer(s) preferences territory and so I am probably overthinking it.
Thanks to yours and other comments, I stopped myself from creating a myriad of separately named variables and started building nonscalar arrays. Cheers.
Stephen23
Stephen23 am 9 Jan. 2024
Bearbeitet: Stephen23 am 9 Jan. 2024
"The intent of the function name question was to ask whether or not function names should be as generic as possible or as specific as desired (including metadata like "fruit" in the name). I think for the falling fruit problem, a very generic function name could be "fallingObjectForce" compared to "fruitForce"."
Ah, I see. That is actually a good question. Personally, I would keep functions (and their names) as general as is reasonable**: it often happens that once you do that, other ways to use your functions or approaches to processing your data will appear.
But for a start just focus on robust data design and efficient code.
** You will note that this just shifts the question to "what is reasonable?" Like all other types of engineering, writing code is ultimately a matter of compromise between many many many factors (e.g. correctness, readability, understandability, debuggability, complexity, expandability, generalizability, runtime, maintenance time, etc.) How to balance those priorities depends on you, your projects, your experience, your goals, etc.
If my answer helped you to resolve your original question then please remember to click the accept button!
Jekazu
Jekazu am 9 Jan. 2024
Hello Stephen,
I accepted your answer. Thanks for your patience and all of the assistance.

Melden Sie sich an, um zu kommentieren.

Weitere Antworten (2)

Matt J
Matt J am 8 Jan. 2024
Bearbeitet: Matt J am 8 Jan. 2024

0 Stimmen

If you had as follows, it would work for any fruit, wouldn't it?
fruit.Mass = 0.15; %kg
fruit.Height = 4; %m

6 Kommentare

Jekazu
Jekazu am 8 Jan. 2024
Hello Matt,
While true, that would mean losing the identity of the particular fruit, i.e. that fruit.Mass and fruit.Height could be for any number of fruit. While I could probably guess that is an apple and not a coconut, if I added another fruit like a peach which is similarly sized to an apple and grows at a similar height, I wouldn't be able to distinguish between the two.
I think I needed to be more clear, I would like to have results for multiple fruit saved at once with the ability to clearly tell which data corresponds to which fruit, hence my variable names of fruit.appleMass and fruit.appleHeight. Perhaps those variable names are also mistakes?
I appreciate the response.
Don't hard-code the type of fruit in the names. It's a piece of data so store it as data.
fruit.type = 'apple';
fruit.Mass = 0.15; %kg
fruit.Height = 4; %m
describeFruit(fruit)
The apple has a mass of 0.15 kg and falls from a height of 4 meters.
fruit2 = struct('type', 'watermelon', 'Mass', 2, 'Height', 3.75);
describeFruit(fruit2)
The watermelon has a mass of 2 kg and falls from a height of 3.75 meters.
function describeFruit(F)
fprintf("The %s has a mass of %g kg and falls from a height of %g meters.\n", ...
F.type, F.Mass, F.Height)
end
Matt J
Matt J am 8 Jan. 2024
Bearbeitet: Matt J am 8 Jan. 2024
While true, that would mean losing the identity of the particular fruit, i.e. that fruit.Mass and fruit.Height could be for any number of fruit.
That's what variable names are for,
apple.Mass = 0.15; %kg
apple.Height = 4; %m
coconut.Mass = 0.23; %kg
coconut.Height = 5; %m
ouch = fruitForce(apple, otherParams);
yikes = fruitVelocity(coconut, otherParams);
Stephen23
Stephen23 am 8 Jan. 2024
Bearbeitet: Stephen23 am 8 Jan. 2024
"That's what variable names are for,"
Ugh, no. Forcing meta-data into the variable names will just make things worse: it will make the OP's task much harder to solve efficiently.
"Perhaps those variable names are also mistakes? "
Yes.
Matt J
Matt J am 8 Jan. 2024
If the name of the fruit is meta-data...
Stephen23
Stephen23 am 8 Jan. 2024
"If the name of the fruit is meta-data..."
It is.

Melden Sie sich an, um zu kommentieren.

Walter Roberson
Walter Roberson am 8 Jan. 2024
Verschoben: Walter Roberson am 8 Jan. 2024

0 Stimmen

What if you did something like
fruit.apple.Mass = 0.15; %kg
fruit.apple.Height = 4; %m
fruit.coconut.Mass = 0.89; %kg
fruit.coconut.Height = 4; %m
then you could use dynamic field name references:
fn = fieldnames(fruit);
for fi = 1 : length(fn)
thisfruit = fn{fi};
deltaHeight = fruit.(thisfruit).Height - otherParams.newtonHeight;
yikes.(thisfruit) = sqrt(2*deltaHeight*otherParams.gravity);
end
and the output would be a struct array with one field for each fruit.

1 Kommentar

Jekazu
Jekazu am 8 Jan. 2024
Hello Walter,
That gives the desired information into a desired format for me. Nested structures seems intuitive, but I hadn't thought about using a cell in a "for" loop before. I really appreciate the response.

Melden Sie sich an, um zu kommentieren.

Kategorien

Mehr zu MATLAB finden Sie in Hilfe-Center und File Exchange

Produkte

Version

R2023b

Gefragt:

am 8 Jan. 2024

Kommentiert:

am 9 Jan. 2024

Community Treasure Hunt

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

Start Hunting!

Translated by