Upload
vanthuy
View
221
Download
4
Embed Size (px)
Citation preview
9.1 Environments and Scope
● There are rules expressing how we link
variable names to values
● Script : names are linked in workspace
● Function: names are linked only inside function
● In general, the “place” in which a variable
is linked is called the variable’s
environment.
● The rules that determine how the linking
takes place are called scoping rules.
9.1 Environments and Scope
● When a function is invoked, a new environment is
created for its variables – both the parameters (input
vars) and the internal variables (“local” vars)
● This environment contains a copy of each input value,
which is why input values cannot be modified.
● This environment is destroyed when the function
returns (exits).
● Scripts do not create a new environment, which is why
they mess with your workspace variables.
9.2 The Debugger
● How do we find bugs in our programs?
● Leave off semicolons so we see intermediate
values
● Use the debugger
● Debugger has two basic features
● Stop your program’s execution
at a crucial “breakpoint”
● Examine values of variables in
the current environment
9.4 Scoping of Functions
● Functions themselves (as opposed to their
parameters and local variables) are available
everywhere – In workspace
– To other functions
● Therefore, if two functions have the same
name (in two different folders), we get a
conflict.
● How to resolve this?
9.4 Scoping of Functions
● Recall the path concept from Chapter 5: to find a file (.m function,
data, etc.), Matlab first looks in the working directory, then
successively in the list of directories in your path (which you can
view/modify by using Set Path...)
● Therefore, name conflicts can be resolved by using the version of
the function first encountered in the path list. This is what Matlab
does.
● But it is a BAD IDEA to rely on this mechanism – i.e, DO NOT re-
use function names if you can avoid that!
When / Why to Write a Function
• Biggest motivation is always : Do not repeat code!
• But there are other reasons – “Segregation” : If a variable is used only once
in your code, maybe you should put that part of
your code into a function, so the rest of the
code doesn't see the variable: recall amortized
smallestfactor function....
function res = smallestfactor(n)
persistent primes
res = n;
if isempty(primes)
primes = [2 3];
end
if primes(end) < sqrt(abs(n)) % we need more primes
for k = primes(end)+2:sqrt(abs(n))
if isprime(k)
primes(end+1) = k;
end
end
end
for k = 1:length(primes) % a different k
if rem(n, primes(k)) == 0
res = primes(k);
break
end
end
function res = smallestfactor(n)
persistent primes
res = n;
if isempty(primes)
primes = [2 3];
end
if primes(end) < sqrt(abs(n))
primes = more_primes(primes, n);
end
for k = 1:length(primes)
if rem(n, primes(k)) == 0
res = primes(k);
break
end
end
function newprimes = moreprimes(oldprimes, n)
newprimes = oldprimes;
for k = oldprimes(end)+2:sqrt(abs(n))
if isprime(k)
newprimes(end+1) = k;
end
end
When / Why to Write a Function
• Biggest motivation is always : Do not repeat code!
• But there are other reasons – Segregation
– Abstraction:
• Maybe you will want to use a different algorithm in
the future . Putting your current algorithm into a
separate function makes it easier to find and
replace your old algorithm quickly.
function newstate = caiter(oldstate)
newstate=zeros(size(oldstate));
% commented-out : yucky!
%for i = 2:(length(oldstate)-1)
% newstate(i)=rule22(oldstate(i-1),oldstate(i),oldstate(i+1));
%end
newstate(2:length(oldstate)-1) = rule22(oldstate(1:end-2), ...
oldstate(2:end-
1), ...
oldstate(3:end));
newstate(1)=rule22(oldstate(end),oldstate(1),oldstate(2));
newstate(end)=rule22(oldstate(end-1),oldstate(end),oldstate(1));
function newstate = caiter(oldstate)
newstate=zeros(size(oldstate));
newstate = update_innerV1(oldstate);
newstate(1)=rule22(oldstate(end),oldstate(1),oldstate(2));
newstate(end)=rule22(oldstate(end-1),oldstate(end),oldstate(1));
function newstate = update_innerV1(oldstate)
for i = 2:(length(oldstate)-1)
newstate(i)=rule22(oldstate(i-1),oldstate(i),oldstate(i+1));
end
function newstate = update_innerV2(oldstate)
newstate(2:length(oldstate)-1) = rule22(oldstate(1:end-2), ...
oldstate(2:end-1), ...
oldstate(3:end));
When / Why to Write a Function
• Biggest motivation is always : Do not repeat code!
• But there are other reasons – Segregation
– Abstraction:
• Maybe you will want to use a different algorithm in
the future . Putting your current algorithm into a
separate function makes it easier to find and
replace your old algorithm quickly.
• Also supports division of labor: Bob writes
primeFactors; Alice writes smallestfactor .
9.6 Warnings and Errors (Skip 9.5)
● Sometimes we want to let the programmer do something
“wrong” without causing the program to crash.
● E.g., divide by zero:
>> x = 1/0;
Warning: Divide by zero
x = Inf
● When writing your own functions, the choice is up to you:
function res = average(vec)
res = sum(vec)/ length(vec);
>> average([]) % ???
9.6 Warnings and Errors
function res = averageV2(vec)
if length(vec) < 1
error('Empty vector, Einstein!')
end
res = sum(vec) / length(vec);
function res = averageV3(vec)
if length(vec) < 1
warning('Empty vector, ain''t no thing')
res = NaN; % not a number
else
res = sum(vec) / length(vec);
end
Catching Errors • Instead of just quitting, we can have our
program respond to an error by doing some kind of “repair” or “panic mode”
• We try to do the operation, and then catch
the error: % compute means of vectors in cell array c
for i = 1:size(c, 1)
try
means(i) = averageV2(c{i});
catch
warning('Empty vector; using NaN mean')
means(i) = NaN;
end
end
9.7 Testing Functions
• eXtreme Programming – “More eyes make shallower bugs” : pair programming
(think about it for your projects!)
– Write your tests before you write your solution
(otherwise, what problem are you solving?)
• Test-cases – Compare to known answers
– Look at “edge cases”
9.7 Testing Functions
• Compare to known answers : e.g., product of prime factors of N should equal N:
>> num = 83723423424323;
>> f = primeFactors(num)
f = 31 109211 2479703
prod(f) == num
ans = 1
9.7 Testing Functions
• Edge cases: what you usually don't consider a typical input to the function
>> primeFactors(2)
ans = 2
>> primeFactors(1)
ans = [] % why?
>> primeFactors(0)
ans = []
>> primeFactors(-10)
ans = []
>> primeFactors(4.1)
ans = 4.1 % ???
%primeFactors(n) - prime factors of n
function factors = primeFactors(n)
factors = [];
remaining = n;
while remaining > 1 % Condition for continuing
sf = smallestfactor(remaining);
factors(end+1) = sf; % Update accumulator
remaining = remaining/sf;
end
% smallestfactor(n) - smallest integer factor
function res = smallestfactor(n)
res = n;
for k=2:sqrt(abs(n))
if rem(n,k) == 0
res = k;
break
end
end
9.7 Testing Functions
• What do edge cases tell us? – Situations (like primeFactors) where we don't
get an error and probably should (0, negative, non-
integer)
– Situations where we do get an error and probably
shouldn't:
function res = findSock(placesToLook)
for i = 1:length(placesToLook)
if contains(placesToLook{i}, 'sock')
res = placesToLook{i};
return
end
end
9.7 Testing Functions
• Large projects will often involve a “test harness” that puts the code through its paces automatically:
function res = testPrimeFactors(ncases)
% test on random #'s, returning problem cases
res = [];
edgecases = [-1 0 1 2 4.1];
randomcases = floor(1000000000000*rand(1,ncases));
for k = [edgecases randomcases]
try
factors = primefactors(k);
if prod(factors) ~= k
res = [res k];
end
catch
res = [res k];
end
end
9.7 Testing: Final Thoughts
• Testing is annoying, but it's much cheaper than paying the cost of untested code later.
• “If there is no way to check the output of your program, ... you have left the realm of scientific computation and entered that of mysticism, numerology, and the occult.”
• Never “program around the problem” (like Captain Crunch).
9.8 Optional and Default Arguments
• Recall plot function: >> plot(x, y, 'ro')
>> plot(x, y, 'b-’)
>> plot(x, y) % same as above
• In general, Matlab functions (should) support
a quoted option which defaults to some
sensible value if unspecified
• This requires use of special local variable
nargin - automagically ignore missing
arguments
function res = nicecos(angle, units)
% “nice” cosine supporting degrees and
% radians (default radians)
if nargin == 1 % one input argument
units = 'radians';
end
switch(lower(units)) % ???
case {'radians', 'rad', 'r'}
res = cos(angle);
case {'degrees', 'deg', 'd'}
res = cos(pi.*angle./180);
otherwise
error('Dammit, Beavis!')
end
function res = nicecos(angle, units)
% “nice” cosine supporting degrees and
% radians (default radians); less redundant
if nargin == 1 % one input argument
units = 'radians';
end
switch(lower(units)) % ???
case {'radians', 'rad', 'r'}
a = angle;
case {'degrees', 'deg', 'd'}
a = pi.*angle./180
otherwise
error('Dammit, Beavis!')
end
res = cos(a);
function res = nicetrig(fcn, angle, units)
% “nice” trig function supporting degrees and
% radians (default radians), multiple functions
if nargin < 3 % two input arguments
units = 'radians';
end
switch(lower(units)) % ???
case {'radians', 'rad', 'r'}
a = angle;
case {'degrees', 'deg', 'd'}
a = pi.*angle./180
otherwise
error('Dammit, Beavis!')
end
res = feval(fcn, a);
9.8 Optional and Default Arguments: Named Arguments
• axes function lets us adjust the axes in a
figure: >> plot(x, y)
>> axes('Position', [.5 .5 2 3], ...
'Units', 'inches')
>> axes('Units', 'inches' , …
'Position', [.5 .5 2 3])
9.8 Optional and Default Arguments: Named Arguments
• axes function lets us adjust the axes in a
figure: >> plot(x, y)
>> axes('Position', [.5 .5 2 3], ...
'Units', 'inches' )
• Requires use of special varargin variable: function axes( varargin )
for i = 1:2:length(varargin)
param = varargin{i};
value = varargin{i+1};
switch (param)
case 'Position':
% etc.