Labfans是一个针对大学生、工程师和科研工作者的技术社区。 | 论坛首页 | 联系我们(Contact Us) |
![]() |
![]() |
#1 |
游客
帖子: n/a
|
![]()
Dynamic Function Creation with Anonymous and Nested Functions
by Stuart McGarrity and Loren Shure When you are developing algorithms to solve technical computing problems, it is often useful to create functions on-the-fly so that you can customize them at run-time without having to define them in files beforehand. For example:
Earlier versions of MATLAB already provided a number of features for managing functions, but there were limitations, for example:
Anonymous and Nested Functions in MATLAB 7 Anonymous functions let you define a function on-the-fly at the command line or in M code, without an associated file. For example, to create a polynomial function, y(x) = 3x2 + 2x + 1 type y = @(x) 3*x.^2 + 2*x + 1; This returns function handle y representing a function of one variable defined by the expression on the right. The variables in parentheses following the @ symbol (in this case, only x) specify variables of which y is a function. The function y can now be evaluated by calling it like any other function (another new feature in MATLAB 7). y(3) ans = 34 You could evaluate this polynomial over a range of points and plot the results together with the previously evaluated point. x=-5:.1:5; plot(x,y(x),'-b',4,y(4),'r*'); title('y(x) = 3*x^2 + 2*x + 1'); xlabel('x'); ylabel('y'); text(4,y(4),'y(4)'); grid ![]() Click on image to see enlarged view. We can create handles to functions of multiple variables. g=@(x,y) (x.^2 + y.^2); ezsurf(g); shading flat; title('f(x,y)=x^2+y^2'); ![]() Click on image to see enlarged view. Nested functions are functions in M-files that are nested inside other functions and which can see the parent function’s workspace. The following function taxDemo.m contains a nested function. function y = taxDemo(income) % Calculate the tax on income. AdjustedIncome = income - 6000; % Calculate adjusted income % Call 'computeTax' without passing 'AdjustedIncome' as a parameter. y = computeTax; function y = computeTax % This function can see the variable 'AdjustedIncome' % in the calling function's workspace y = 0.28 * AdjustedIncome; end end The nested function computeTax can see the variables in the parent function's workspace, in this case AdjustedIncome and income. This makes sharing of data between multiple nested functions easy. We can call the function in the usual way. % What is the tax on income of 80,000? tax=taxDemo(80e3) tax = 2.0720e+004 The ability of nested functions to see into their parent's workspace enables you to control the scope of your variables, letting them be accessed by multiple functions, while avoiding the side effects of us ing a global variable. At the same time, you can reduce your memory requirements by sharing large data sets in a controlled way. An end statement is required at the end of all nested functions, making them different from traditional local subfunctions. However, all functions in a file containing nested functions, including traditional subfunctions, require termination with an end statement. Functions can be nested to any level. Customizing Functions You can create different variants or customized versions of the same function with anonymous functions or nested functions. For example, if we want to create a function like y = ax2 + bx + c and customize a, b, or c at runtime, we write a=3; b=2; c=-10; y1=@(x) a*x.^2 + b*x + c; Any variables in the function that are not listed in parentheses are taken from the calling workspace at the time of definition. Now let's change a, b, or c and generate other customized functions. For example, a=-5; b=1; c=20; y2=@(x) a*x.^2 + b*x + c; Evaluate or plot these as before. x=-5:.1:5; plot(x, y1(x), x, y2(x)); legend('y_1(x)=3x^2+2x-10','y_2(x)=-5x^2+x+20'); xlabel('x'); ylabel('y_1 and y_2'); title('y(x) = a*x^2 + b*x + c'); grid ![]() Click on image to see enlarged view. Since the definitions of y1 and y2 included the values of a, b, and c at the time the function handles are created, they are robust to variations in the workspace such as changed or deleted values of a, b, or c. a=100; y1(3) ans = 23 a=5000; y1(3) ans = 23 Let's see how we can carry out a similar task with nested functions by looking at makefcn.m, which contains a nested function. function fcn = makefcn(a,b,c) % This function returns a handle to a customized version of 'parabola'. % a,b,c specifies the coefficients of the function. fcn = @parabola; % Return handle to nested function function y = parabola(x) % This nested function can see the variables 'a','b', and 'c' y = a*x.^2 + b.*x + c; end end When you call makefcn, it returns a function handle to the internal nested function, which has been customized according to the parameters passed to the parent function. For example, f1 = makefcn(3,2,10); f2 = makefcn(0,5,25); We can evaluate these two different functions as before. f1(2) ans = 26 f2(2) ans = 35 In general, anonymous functions are better for quick, on-the-fly simple function creation and customization. Nested functions are more suited to more complex function customization and management. Working with Function Functions You can pass nested and anonymous functions to optimization or integration routines, known as function functions in MATLAB. For example, to find the area under the curve f1 between 0 and 4, use areaunder=quad(f1,0,4) areaunder = 120.0000 By having the integrand f1 defined beforehand, you keep its arguments (such as the polynomial coefficients) separate from those of the integrator quad (such as the limits of integration). This can eliminate potential confusion. We will plot f1 again together with a plot of the area just calculated. x=-5:.1:5; plot(x,f1(x)); hold on x=0:.1:4; area(x,f1(x),'Facecolor','g'); % You can evaluate f without feval hold off title('Area under f_1(x), between 0 and 4'); grid ![]() Click on image to see enlarged view. Handling Dynamic Function State If you need to create a function that retains dynamic state, you can use a nested function and store its state in the parent's workspace. Functions with dynamic state can represent algorithms that range from simple counters to components of a large system. Simple Function State Let's create a counter function that returns a number which increments each time it is called. Let's look at the function makecounter.m. function countfcn = makecounter(initvalue) % This function returns a handle to a customized nested function 'getCounter'. % initvalue specifies the initial value of the counter whose handle is % returned. currentCount = initvalue; % Initial value countfcn = @getCounter; % Return handle to getCounter function count = getCounter % This function increments the variable 'currentCount', when it % gets called (using its function handle). currentCount = currentCount + 1; count = currentCount; end end When you call makecounter, it returns a handle to its nested function getCounter. getCounter is customized by the value of initvalue, a variable it can see via nesting within the workspace of makecounter. Let's make a couple of counter functions. counter1 = makecounter(0); % Define counter initialized to 0 counter2 = makecounter(10); % Define counter initialized to 10 Here we have created two customized counters: one that starts at 0 and one that starts at 10. Each handle is a separate instance of the nested function and its calling workspace. Note counter1 does not take any parameters. We need to use the parentheses to invoke the function instead of looking at the function handle variable itself. counter1Value=counter1() counter1Value = 1 We can call the two functions independently as there are two separate workspaces kept for the parent functions. They remain in memory while the handles to their nested functions exist. Each time the counter functions are called, the associated variable currentCount is incremented. counter1Value=counter1() counter1Value = 2 counter2Value=counter2() counter2Value = 11 Complex Function State You can use nested functions to store a more complex dynamic state such as that of a filter. Let's look at the function makeFilter.m. function filterhandle = makeFilter(b,a) % Initialize State state=zeros(max(length(a),length(b))-1,1); % Return handle to filter function filterhandle=@callFilter; function output = callFilter(input) % Calculate output and update state [output,state] = filter(b,a,input,state); end end The function makeFilter returns a handle to a nested function (callFilter) that performs a filtering operation and maintains its state in the parent's workspace. The filter state is initialized when the function handle is created, then gets updated each time the nested function is called. The nested function also has to calculate the output of the filtering operation and return it as its output argument. You can call this function in a for-loop many times, as shown in simplesystem.m. %% System Parameters frameSize = 1000; sampleTime = 1e-3; %% Initialize System Components and Component Parameters filter1 = makeFilter([0.02,0,-0.23,0,0.49,0,-0.23,0,0.02],1); filter2 = makeFilter([-0.05 0.13 0.83 0.13 -0.05],1); randSource = makeRandSource(frameSize); scope = makeScope(sampleTime,[-2 2],'My Scope'); %% Simulation Loop for k = 1:100 signal1 = randSource(); signal2 = filter1(signal1); signal3 = filter2(signal2); scope(signal3) end The main for-loop in the simulation now becomes very clean and simple where the only variables passed between the functions (signal1, signal2, and signal3) are pure data, without being mixed up with other function parameters (such as filter coefficients) and state. The makeFilter routine could be expanded to return more than one function, such as one to return the filter state, or even one to reset the filter. These function handles could be returned as multiple output arguments or as a structure, where each field is a function handle. The functions can then be called in a manner similar to evaluating the methods of an object. Other Uses of Nested and Anonymous Functions The function handling capabilities of nested and anonymous functions have many other uses. Function Composition You can create handles to functions of functions, allowing a very natural mathematical notation for function composition, such as f=@(x) x.^2; g=@(x) 3*x; h=@(x) g(f(x)); % Equivalent to 3*(x^2) h(3) ans = 27 Memoization We can store the previously computed return values of a function in a "table" for later use instead of recalculating values. This is helpful if the function is computationally intensive. Let's look at a function that stores computed values and makes them available for reuse. function f = memoize(F) % One argument F, inputs testable with == % Scaler input to F x = []; y = []; f = @inner; function out = inner(in) ind = find(in == x); if isempty(ind) out = F(in); x(end+1) = in; y(end+1) = out; else out = y(ind); end end end Here's how to use this function. First you create a "memoized" version of the function for which you want to remember the return values, for example, sin. f = memoize(@sin) f = @memoize/inner Let's call the function a few times. f(pi/2) ans = 1 f(pi/4) ans = 0.7071 f(pi/8) ans = 0.3827 The returned values, in this case sin(pi/2), sin(pi/4), and sin(pi/8) are stored. To see how this is working, let's use the functions command to inspect the state of the function handle f. functionInfo = functions(f) functionInfo = function: 'memoize/inner' ype: 'nested' file: [1x76 char] workspace: {[1x1 struct]} functionInfo.workspace{1} ans = f: @memoize/inner F: @sin x: [1.5708 0.7854 0.3927] y: [1 0.7071 0.3827] Now if you request a previously computed result, such as for sin(pi/4), the value is taken from the table without a new value being added. Here you can see the stored workspace doesn't change. f(pi/4) ans = 0.7071 functionInfo = functions(f); functionInfo.workspace{1} ans = f: @memoize/inner F: @sin x: [1.5708 0.7854 0.3927] y: [1 0.7071 0.3827] Data Structures Nested functions can be used to create data structures such as lists and trees. Find Sturla Molden's example in the comp.soft-sys.matlab Usenet newsgroup. The new dynamic function creation and handling features of nested and anonymous functions have made functions fully-fledged members of the MATLAB language and opened up a whole new set of programming patterns. For more information on functions and function handles in MATLAB, see the following resources. 更多... |
![]() |