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 件のコメント

Stephen23
Stephen23 2024 年 1 月 8 日
編集済み: Stephen23 2024 年 1 月 8 日
"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 2024 年 1 月 8 日
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 2024 年 1 月 8 日
編集済み: Stephen23 2024 年 1 月 9 日
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.

サインインしてコメントする。

 採用された回答

Stephen23
Stephen23 2024 年 1 月 8 日
編集済み: Stephen23 2024 年 1 月 9 日

3 投票

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 件のコメント

Jekazu
Jekazu 2024 年 1 月 9 日
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 2024 年 1 月 9 日
編集済み: Stephen23 2024 年 1 月 9 日
"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 2024 年 1 月 9 日
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 2024 年 1 月 9 日
編集済み: Stephen23 2024 年 1 月 9 日
"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 2024 年 1 月 9 日
Hello Stephen,
I accepted your answer. Thanks for your patience and all of the assistance.

サインインしてコメントする。

その他の回答 (2 件)

Matt J
Matt J 2024 年 1 月 8 日
編集済み: Matt J 2024 年 1 月 8 日

0 投票

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

6 件のコメント

Jekazu
Jekazu 2024 年 1 月 8 日
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.
Steven Lord
Steven Lord 2024 年 1 月 8 日
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 2024 年 1 月 8 日
編集済み: Matt J 2024 年 1 月 8 日
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 2024 年 1 月 8 日
編集済み: Stephen23 2024 年 1 月 8 日
"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 2024 年 1 月 8 日
If the name of the fruit is meta-data...
Stephen23
Stephen23 2024 年 1 月 8 日
"If the name of the fruit is meta-data..."
It is.

サインインしてコメントする。

Walter Roberson
Walter Roberson 2024 年 1 月 8 日
移動済み: Walter Roberson 2024 年 1 月 8 日

0 投票

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 件のコメント

Jekazu
Jekazu 2024 年 1 月 8 日
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.

サインインしてコメントする。

カテゴリ

ヘルプ センター および File ExchangeMATLAB についてさらに検索

製品

リリース

R2023b

質問済み:

2024 年 1 月 8 日

コメント済み:

2024 年 1 月 9 日

Community Treasure Hunt

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

Start Hunting!

Translated by