Internal variables not calculated

3 visualizzazioni (ultimi 30 giorni)
Steven Wolf
Steven Wolf il 22 Gen 2020
Commentato: Steven Wolf il 24 Gen 2020
I don't understand why this code works:
% Important constants and variables. All units SI unless noted otherwise.
k = 8.99e9;
q = 1e-6; % Point charge magnitude
Q = 1e-3; % Rod charge magnitude
L = 1; % Rod length
xPoint = 0.25; % Distance from center of rod to point charge
N = 10;
% Initialize the force
f = [0, 0];
% Calculate the magnitude of each charge in the rod given N chunks
dQ = Q/N;
% Calculate the separation between charges
% (Depends on method, value is incorrect)
chargeSep = L/N;
% y coordinate of top charge (Depends on method, value is incorrect)
y = L/2 - chargeSep/2;
for i = 1:N
% For the given coordinates, find the vector to the point charge
% from the ith charge on the rod (values are incorrect)
rVec = [xPoint, -y];
rMag = sqrt(xPoint^2 + y^2);
rHat = rVec/rMag;
% Calculate the force for this particular point charge using
% Coulomb's law
dF = k*dQ*q/rMag^2 * rHat;
% Add to the total force
f = f + dF;
% Advance to the next charge on the rod
y = y - chargeSep;
end
f
(f = 64.3779 0)
While this one doesn't
% Important constants and variables. All units SI unless noted otherwise.
k = 8.99e9;
q = 1e-6; % Point charge magnitude
Q = 1e-3; % Rod charge magnitude
L = 1; % Rod length
xPoint = 0.25; % Distance from center of rod to point charge
rodForce(10)
% Make a function that calculates the force on the rod given the number of
% chunks. For now, we will make the only input the number of chunks
function f = rodForce(N)
% Declare global variables
global Q
global xPoint
global q
global L
global k
% Initialize the force
f = [0, 0];
% Calculate the magnitude of each charge in the rod given N chunks
dQ = Q/N;
% Calculate the separation between charges
% (Depends on method, value is incorrect)
chargeSep = L/N;
% y coordinate of top charge (Depends on method, value is incorrect)
y = L/2 - chargeSep/2;
for i = 1:N
% For the given coordinates, find the vector to the point charge
% from the ith charge on the rod (values are incorrect)
rVec = [xPoint, -y];
rMag = sqrt(xPoint^2 + y^2);
rHat = rVec/rMag;
% Calculate the force for this particular point charge using
% Coulomb's law
dF = k*dQ*q/rMag^2 * rHat;
% Add to the total force
f = f + dF;
% Advance to the next charge on the rod
y = y - chargeSep;
end
end
When I examine the output it seems that my variables dQ, chargeSep, etc (those calculated within the function) are not used. This is maddening.

Risposta accettata

Steven Wolf
Steven Wolf il 23 Gen 2020
Ok, I'm writing my own answer. The folks who answered previously were helpful, but I'm putting it together for anyone else who might stumble across this. The answer is that Matlab assumes that your function code is in a separate file. Because of this, variables that you want to pass between a calculation script file and a function definition file need to be explicitly declared as global variables in both places. So Matlab treats the single file like this:
File 1 (calculation script):
% Declare global variables and define them
global k q Q %etc
k = 8.99e9;
q = 1e-6;
%...
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% In the full script, I'm doing things with this function and these constants not
% important to this discussion/problem. I need to use the global variables in these calculations.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
File 2 (function script):
function f=rodForce(N)
% Declare global variables (same as above)
global k q Q %etc
% Then do the function as in the OP
end
Or you could combine the two files as I have done (in the comment to a different answer). But global variables need to be declared both places (before the variables are defined and inside the function).
This is different from other programming languages (such as R and Python) when variables that aren't defined within a function are automatically inherited from the global environment (unless you restrict the environment within the funciton definition). And if no variable exists with that name, the error message is explicit.
  3 Commenti
Steven Lord
Steven Lord il 23 Gen 2020
But global variables need to be declared both places (before the variables are defined and inside the function).
That's if you choose to use global variables. Instead of doing that, you should prefer to pass data into your functions as input arguments and out from your functions as output arguments.
If you pass your data using global variables, any code that has access to the global workspace (which most if not all the code you run in MATLAB will) has the ability to affect the operation of your function. Consider if an extremely mischievous coworker put something like the following code in your startup.m file:
start(timer('TimerFcn', @(~, ~) modifyGlobalVariables, ...
'ExecutionMode', 'fixedRate', ...
'ObjectVisibility', 'off'));
function modifyGlobalVariables
D = who('global');
if ~isempty(D)
eval("global " + join(D));
for whichGlobal = 1:numel(D)
eval(D{whichGlobal} + " = rand();")
end
end
end
Granted this is quite an extreme example, but with this all your global variables will take a new random value every second. Good luck debugging why your code isn't doing what you expect!
global a
a, pause(1), a % Why did the value of a change?!
As a more realistic example, let's say some other script or function also decides (unbeknonwst to you) that it needs to "pass" variables named k, q, and/or Q using global into a function it calls. Your function may assume that the values of k, q, and Q remain what they were after you last ran your script, but if you'd run that other script or function that assumption is no longer valid. Again, such a problem likely would be very difficult to debug.
Steven Wolf
Steven Wolf il 24 Gen 2020
I do like the nested function option presented below--I would probably prefer it if I were using this for my own project. For my particular use case, I don't see it as ideal though. This is for a (physics) class I'm teaching, and I'd rather deal with global variables than nested functions for two reasons.
  1. One function is already at the limit of what most students are comfortable with.
  2. My students will often work in a combination between the interpreter and a script. For this reason in particular, messy global variables become vital global variables.
My job is to teach them physics, and while doing this, I encourage them to use code. I will say, mischevious coworkers don't exist for this case. (You have collaborators, and if someone adds malicious code as suggested, they harm themselves as this is a group assignment with group grading.)
I often use global variables when I code in Python or R. I also make the practice of not automatically loading the previous global environment. I keep an environment from different projects/cases separate and load them intentionally if I have variables that I want to persist between sessions. And I teach my students to do that so that they can avoid the problems that you point out.

Accedi per commentare.

Più risposte (2)

James Tursa
James Tursa il 22 Gen 2020
You didn't declare those variables global in the caller. You only have them global in the function. Add the global statements in the caller.
That being said, it would be better to pass these variables in the argument list as parameters instead of making them global.
  6 Commenti
Walter Roberson
Walter Roberson il 22 Gen 2020
Insert
global Q
global xPoint
global q
global L
global k
just before you assign values to the variables.
Steven Wolf
Steven Wolf il 23 Gen 2020
I see. It seems like there is no advantage in Matlab for having your function be in the same file as your script. In fact, it seems like the idea is to actively discourage this behavior. The documentation should be altered to reflect this. This is the page I found:
So it is working now like this:
% Declare global variables and define them
global k q Q %etc
k = 8.99e9;
q = 1e-6;
%...
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% In the full script, I'm doing things with this function and these constants not
% important to this discussion/problem
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Then define the function
function f=rodForce(N)
% Declare global variables (same as above)
global k q Q %etc
% Then do the function as before
end

Accedi per commentare.


James Tursa
James Tursa il 23 Gen 2020
Maybe the behavior you are looking for is nested functions. Instead of a function at the end of a script, you have a function within another function. The nested function sees all of the parent function variables without the use of global variables. E.g.,
function parentfun
x = 5;
childfun;
function childfun
disp(x);
end % child
end % parent
And a sample run
>> parentfun
5
So you can see that childfun sees the variable x that is defined in parentfun without the use of global statements.
  1 Commento
Stephen23
Stephen23 il 23 Gen 2020
Modificato: Stephen23 il 23 Gen 2020
+1 This is the best solution by far. It is simple and without messy global variables.

Accedi per commentare.

Categorie

Scopri di più su Mathematics in Help Center e File Exchange

Prodotti


Release

R2019b

Community Treasure Hunt

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

Start Hunting!

Translated by