3
\$\begingroup\$

I've made a script (Triangle.m) to calculate the area of a triangle in MATLAB:

function Triangle()
 % Main function
 clc;
 clear variables;
 close all;
 % Set function values
 base = force_positive_finite("Base");
 height = force_positive_finite("Height");
 area = tri_area(base,height);
 fprintf("Area: %2.3f m2\n",area);
end
function output = force_positive_finite(var_name)
% Ask/force the user to enter a positive number
 output = -1;
 while true
 output = input("Enter in "+var_name+" (m): ");
 if input_error_response(output)
 % If no error, leave loop
 break 
 end
 end
end
function response = input_error_response(in)
 % Warns user if input is invalid
 if isempty(in) || in < 0 || in==inf % Value must be a non-empty positive number
 disp("Enter a value: {0 <= VALUE < inf}");
 response = false; % This signals the loop to continue
 else
 response = true; % This means we can exit the loop
 end
end
function area = tri_area(base,height)
 % Calculate the area of a triangle
 arguments
 base {mustBeNumeric, mustBeFinite};
 height {mustBeNumeric, mustBeFinite};
 end
 area = base*height*0.5;
end

It contains force_positive_finite to ensure the input is >= 0 and finite, then is fed into tri_area where it is checked to be numeric and finite, before calculating the area.

I am new to MATLAB, so any pointers on convention, optimisation and general improvements are appreicated.

asked Mar 7, 2023 at 0:07
\$\endgroup\$
2
  • \$\begingroup\$ "clear variables;" What?!? You do three clears for no perceptible reason? I won't be calling your function anytime soon, for fear I lose that giant matrix that took ten minutes to create. More generally, think about whether you're writing a (clean! pure!) function that maps from these numbers to those numbers, versus writing an application that satisfies some customer's Story. The three clears is an extreme example, but even a side effect like an input() interaction is usually something you want to confine to the very top-level application, and keep it out of utility functions. \$\endgroup\$ Commented Mar 7, 2023 at 0:26
  • \$\begingroup\$ @J_H this is literally my first script, I didn't really know how potent the 3 clears were. I'll reduce it to a clc, thank you \$\endgroup\$ Commented Mar 7, 2023 at 1:51

1 Answer 1

3
\$\begingroup\$

The function Triangle() is clearly a script that you turned into a function. I personally don't like putting clc, clear and close at the top of my scripts, but I see this a lot, and it's not a terrible thing to do. In a function they are very much out of place.

A function has its own workspace (it cannot see variables defined outside of it), so there really is no need for a clear. This is the point of using functions instead of scripts: each function has its own workspace ("scope" in other programming languages), which is cleared the moment the function ends. The base workspace doesn't get cluttered with variables if you use functions instead of scripts, and the code inside the function will not accidentally be using variables from a different scope (which makes finding bugs easier).

Given that Triangle() asks for user input, a clc is not terrible, but you could accomplish the same effect (make the input prompt more visible) by first printing some empty lines.

close all closes all figure windows. Your program doesn't deal with figure windows, why attack any existing ones? Just let them be!


force_positive_finite() is fine. Of course there are other ways to implement it. Instead of break, for example, you could return. This makes it a bit easier (IMO) to read the function, because I don't need to look for additional code below the loop. I know this is where the function ends. But simpler would be:

function output = force_positive_finite(var_name)
 % Ask/force the user to enter a positive number
 output = -1;
 while ~input_error_response(output)
 output = input("Enter in "+var_name+" (m): ");
 end
end

while tests a condition. Instead of invalidating that test by testing true, and adding an additional if statement, use that test to do what your if statement does.


Similarly, input_error_response() can be simplified a bit. The construct if <test>, v = true, else, v = false is a typical anti-pattern, better written as v = <test>. You do a bit more inside your if statement, still you can avoid assigning true or false to your response variable:

function response = input_error_response(in)
 % Warns user if input is invalid
 response = ~isempty(in) && in >= 0 && isfinite(in)
 if ~response
 disp("Enter a value: {0 <= VALUE < inf}");
 end
end

Note that your comment "Value must be a non-empty positive number" comes next to a test for the number being non-negative, which is not the same as positive. Positive: in > 0. Non-negative: in >= 0. [Your original test, in < 0 is negated as in >= 0, as I put into my version of the function.]

I changed the in == inf test to ~isfinite(in). This catches also the case where in == -inf (which you already tested for with the non-negativity test), and the case where in is NaN.

Finally: What if the user inputs "foo"? What if the user inputs [4, 6]? You should probably also test that the input is a double array (isnumeric(in) or isfloat(in) or isa(in,'double')), and scalar (isscalar(in)).

answered Mar 13, 2023 at 15:56
\$\endgroup\$
2
  • \$\begingroup\$ Thank you for such a detailed response, I have updated with the relevant changes. How would I go about implementing isdouble()? do I need to convert the input to an fi object? \$\endgroup\$ Commented Mar 13, 2023 at 22:11
  • 1
    \$\begingroup\$ @FreddyMcloughlan Sorry, my mistake. I didn't pay attention to the details when I looked up if isdouble existed. isnumeric is probably the way to go, but you can be more detailed if you like (see updated answer). Oh, and take a look at isreal as well, in case the user enters a complex number. \$\endgroup\$ Commented Mar 13, 2023 at 22:17

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.