Is it possible to write several statements into an anonymous function?
154 visualizaciones (últimos 30 días)
Mostrar comentarios más antiguos
Hi all,
I would like to write the following statements into a function
z=zeros(5);
z(1,1)=x(1)*cos(x(2));
z(3,4)=log(x(3));
Is is possible to write this into an anonymous function somehow?
thanks,
Patrick
9 comentarios
PhilDD
el 23 de Feb. de 2016
Why is everybody asking, why he wants to do that instead of helping him?
I was actually looking for a similar thing. Anonymous functions might not be as fast as functions in m-Files, but for a quick'n'dirty solution, where speed doesn't matter, they might do their job, are easily written down, without spamming your folder with lots of m-Files...
Respuestas (11)
Walter Roberson
el 8 de Oct. de 2012
Editada: Walter Roberson
el 8 de Oct. de 2012
Also, an if/else can be coded in function form by using
FHEXEC = @(FH) FH();
FHSELECT = @(TF,CONDITION) TF(CONDITION==[true,false]);
IF = @(CONDITION,TRUEFUNC,FALSEFUNC) FHEXEC( FHSELECT([TRUEFUNC,FALSEFUNC],CONDITION) )
Then, for example,
if x>3; sin(x); else cos(x); end;
can be coded as
IF(x>3, @(sin(x)), @(cos(x)))
Jamie
el 16 de Abr. de 2016
Anonymous functions support only expressions, so multiple-statement procedural code must be transformed into functional style. The pain points in this transformation are control statements, such as if/then, which are not available, and assignment, which is unnatural at best in functional languages. (Matlab-without-statements could be considered a functional language, albeit an awful one.)
One way to think of the procedural-to-functional transformation is to imagine each statement (potentially with side-effects) as a function applied to the environment that returns a new environment.
To take your example, I might use a structure 's' to represent a local environment, and each statement transforms the environment. Stealing from Walter's clever helper functions:
ASGN = @(A,B,S) subsasgn(A, struct('type', '()', 'subs', {B}), S);
then:
x = [ 1 2 3 ];
f1 = @(s) setfield(s, 'z', zeros(5)); % z = zeros(5);
f2 = @(s) setfield(s, 'z', ASGN(s.z, {1,1}, x(1)*cos(x(2)))); % z(1,1)=x(1)*cos(x(2));
f3 = @(s) setfield(s, 'z', ASGN(s.z, {3,4}, log(x(3)))); % z(3,4)=log(x(3));
f4 = @(s) s.z; % equivalent to "return z"
f4(f3(f2(f1(struct()))))
To invoke functions that have no output value as statements, we need a "no-op" function that passes the environment unmodified but forces evaluation of other parameters:
nop = @(s, varargin) s; % evaluates other arguments while passing environment unmodified
x = [ 1 2 3 ];
f1 = @(s) setfield(s, 'z', zeros(5)); % z = zeros(5);
f2 = @(s) setfield(s, 'z', ASGN(s.z, {1,1}, x(1)*cos(x(2)))); % z(1,1)=x(1)*cos(x(2));
f3 = @(s) setfield(s, 'z', ASGN(s.z, {3,4}, log(x(3)))); % z(3,4)=log(x(3));
f4 = @(s) nop(s, fprintf('hello world\n')); % fprintf('hello world\n');
f5 = @(s) s.z; % equivalent to "return z"
f5(f4(f3(f2(f1(struct())))))
To perform if/then, again I'll borrow Walter's idea: if we're operating on continuations we can implement a ternary operator to choose the appropriate continuation, and then evaluate it. If I have
ftrue = @(s) setfield(s, 'z', ASGN(s.z, {2,2}, 99));
ffalse = @(s) setfield(s, 'z', ASGN(s.z, {3,3}, 10));
and a simple ternary (without short-circuit)
ternary = @(c,varargin) varargin{2-logical(c)}; % returns second or third argument
then I can implement a conditional which applies one or the other continuations
ftrueorfalse = @(s) feval(ternary(s.z(1,1) < 0, ftrue, ffalse), s);
Finally to avoid assigning temporary variables just to be able to "f5(f4(f3(f2(f1(struct())))))", we can implement the equivalent of a begin/end construct that takes any number of @(s)... style functions and composes them and returns single @(s)... function. This will also be necessary for multiple statements in an if/else block. For simplicity these helper functions are not anonymous:
% given statements, generate function that applies all of them to an environment
function f = compose(varargin)
f = @(s) applyeach(s, varargin{:});
end
% given environment and statements, apply each statement to the environment in turn
function s = applyeach(s, varargin)
for i=1:length(varargin)
s = varargin{i}(s);
end
end
Finally, all together, procedural code can be more-or-less one-to-one translated into a (admittedly ugly) composition of functions, for example:
x = [ 1 2 3 ];
z = zeros(5);
z(1,1) = x(1)*cos(x(2));
z(3,4) = log(x(3));
fprintf('hello world\n');
if z(1,1) > 0
fprintf('true branch\n');
z(2,2) = 99;
else
fprintf('false branch\n')
z(3,3) = 10;
end
% return z
Translates into:
x = [ 1 2 3 ];
f = @() feval(compose(...
@(s) setfield(s, 'z', zeros(5)), ...
@(s) setfield(s, 'z', ASGN(s.z, {1,1}, x(1)*cos(x(2)))), ...
@(s) setfield(s, 'z', ASGN(s.z, {3,4}, log(x(3)))), ...
@(s) nop(s, fprintf('hello world\n')), ...
@(s) feval(ternary(s.z(1,1) > 0, ... % "if"
compose( ... % "begin block"
@(s) nop(s, fprintf('true branch\n')), ...
@(s) setfield(s, 'z', ASGN(s.z, {2,2}, 99)) ...
) ... % "end block"
, ... % "else"
compose( ... % "begin block"
@(s) nop(s, fprintf('false branch\n')), ...
@(s) setfield(s, 'z', ASGN(s.z, {3,3}, 10)) ...
) ... % "end block"
), s), ... % "end if"
@(s) s.z ... % "return z"
), struct());
Now, as for whether this is desirable? It depends. Clearly the syntax is ugly, so you wouldn't want to do this more than you had to. Speed is an open question but might not matter depending on the application.
To me it seems the snide objections to the original question are mostly ignorant of why you would want to use anonymous functions at all, much less why multi-statement anonymous is somehow never needed while single-expression anonymous is useful. Yes, much of what anonymous functions can do is also possible with inner (nested) functions, but this also argues that there is no point to anonymous functions at all, since you can always just define a one-line inner function and refer to it.
I can see at least a couple reasons for anonymous functions instead of inner functions that go beyond single statements:
- Anonymous functions bind variables from the outer scope at the time they are defined, unlike inner functions that use variables from the outer scope at the time they are used. Variables from the outer scope that are used in inner functions can be dangerous in that the scope of effect is more difficult to trace, akin to global variables. Anonymous functions do not suffer from this.
- Some algorithms are much more intuitive when written procedural style with multiple assignments.Patrick's original question is an example of this, and while equivalent behavior can be achieved with reshape, it is less clear what the output will be.
Regards,
-Jamie
4 comentarios
Walter Roberson
el 2 de Mayo de 2016
It can be done with evalc()... or let me see
xyxy = @() set(gcf, 'visible','on');
pqr = feval(xyxy)
pqr =
[]
Jamie
el 2 de Mayo de 2016
Hmm, it appears that set() seems to have its own rules, which are version-dependent. 2014a blows up on your feval(xyxy) but 2015b succeeds, and x = set(gcf, 'visible','on'); also succeeds in 2015b. But even in 2015b, if I define my own 'noret' function (not anonymous)
function noret(a, b) % no return value
fprintf('a, b: %d %d\n', a, b);
end
then feval of an anonymous function that calls noret still fails on too many output arguments.
The best I could do is to make my own (non-anonymous) feval that does not attempt to get an output value and just returns a dummy value so it will not barf when used in an expression.
function dummy = feval_discard(f, varargin)
feval(f, varargin{:}); % nargout will be zero
dummy = 0;
end
I'm stumped as to how to do this 'anonymously'.
Matt J
el 8 de Oct. de 2012
No, anonymous functions have to consist of a single statement, but your commands can be expressed as a single statement this way
z=@(x) reshape( [x(1)*cos(x(2)), zeros(1,16), log(x(3)), zeros(1,7)], [5,5]);
Not sure why you wouldn't just put it in a normal function or nested function, though.
6 comentarios
per isakson
el 8 de Oct. de 2012
Do you assume that "reparsing each specification and writing a separate file in each case" is a problem because of the time it takes?
"I can do it without knowing in advance how many specifications". When and how is data for the new specifications supplied?
Walter Roberson
el 8 de Oct. de 2012
z = @(x) subsasgn(subsasgn(zeros(5), struct('type', '()', 'subs', {1,1}), x(1)*cos(x(2)))), struct('type', '()', 'subs', {3,4}), log(x(3));
(Note: bracket count might be wrong)
You might want to use an auxillary function:
ASGN = @(A,B,S) subsasgn(A, struct('type', '()', 'subs', B), S);
z = @(z) ASGN(ASGN(zeros(5), {1,1}, x(1)*cos(x(2)))), {3,4}, log(x(3)));
0 comentarios
Daniel Shub
el 8 de Oct. de 2012
It is a little odd but ...
f = @(x)(reshape([x(1)*cos(x(2)), zeros(1, 16), log(x(3)), zeros(1, 7)], [5, 5]));
0 comentarios
Andrew Cramer
el 27 de Mzo. de 2013
Editada: Walter Roberson
el 27 de Mzo. de 2013
A little old but I have a solution that may be interesting to some.
Solution:
I wrote a function ExecMany(varargin) which executes each of the function handles supplied to it.
Why did I want to?
I wanted this functionality when making a GUI, by using anonymous functions I could hard code the guicontrol handles into the anonymous function which gets made when specifying callbacks.
- A nested function could do it but when the main function finishes the variables are no longer guaranteed to exist, what happens when a nested function is called outside its parent function?
- A function at the end of the file or in the working directory could do it but it wouldn't know what handles to use
- Global variables could fix that, but that's messy, inelegant and the user of my GUI gets to see all my variables and play with them.
- Reshape, or making an array etc doesn't work for functions like set(...) who don't return anything.
- Anonymous functions can have the handles hard coded, don't pollute the global namespace and don't make gratuitous numbers of files of 1 line functions. In addition, the definition of the function is close to the uicontrol, in some cases, it can be defined along with the uicontrol.
Muthu Annamalai
el 8 de Oct. de 2012
Great answers everyone; the best solution for this problem might still be 'reshape' based answer by Walter, Daniel and Matt - each separately.
I would think easiest way to achieve multiple statement anonymous functions should be through evaluating them, i.e. using evalc().
Its not a great idea, but its something that works.
2 comentarios
Matt J
el 8 de Oct. de 2012
Editada: Matt J
el 8 de Oct. de 2012
I would think easiest way to achieve multiple statement anonymous functions should be through evaluating them, i.e. using evalc().
Then you obviously overlooked my comment on nested functions. Nothing could be easier or more direct. Sub-functions are another option.
N/A
el 15 de Oct. de 2015
Editada: N/A
el 15 de Oct. de 2015
This case is old, but I stumbled upon it today. The easiest way to get multiple statements into an anonymous function, is by using the eval function (or evalin). This is not fast nor space saving, but it could be useful in cases where your MATLAB program is (MATLAB compiler) compiled into an executable, and you can input anonymous functions by text.
@(x)eval('disp(1);disp(2)');
Arlon
el 1 de Dic. de 2023
You can sort-of do it, I found three ways by starting with a recursive anonymous function. The recursion is used not for recursion per-se, but only to accomplish this idea of creating a multi-line anonymous function - because something different overall than recursion happens - the only thing really recursioning is various operations on the parameters, as they are being passed from line-to-line. Branching passes the parameters from recursive iteration to recursive iteration, updating the parameters each iteration. Branching conditionals select which iteration will operate on the parameters. I did also replace 'z=' with a new matrix built from z and the replacement element.
%============ Method 1: ====================================================
% built out of a recursive anonymous function as described at https://stackoverflow.com/questions/32237198/recursive-anonymous-function-matlab
if_ = @( pred_, cond_ ) cond_{ 2 - pred_ }(); % from: https://stackoverflow.com/questions/32237198/recursive-anonymous-function-matlab
makeZ=@(makeZ,x) ...
if_(~isfield(x,'i') ...
,{@()makeZ(makeZ,struct('i',1,'x',x ,'z',zeros(5))) ...
,@()if_(x.i==1 ...
,{@()makeZ(makeZ,struct('i',0,'x',x.x ,'z',[x.x(1)*cos(x.x(2)) x.z(1,2:5);x.z(2:5,:)] )) ...
,@() [x.z(1:2,:);x.z(3,1:3) log(x.x(3)) x.z(3,5) ;x.z(4:5,:)] })});
makeZ(makeZ,[2 3 4])
% or
mkZ=@(x)makeZ(makeZ,x);
mkZ([2 3 4])
%============= Method 2: ==================================================
% built out of a recursive anonymous function as described at https://blogs.mathworks.com/loren/2013/01/24/introduction-to-functional-programming-with-anonymous-functions-part-2/
% helper functions iif & recur from https://blogs.mathworks.com/loren/2013/01/24/introduction-to-functional-programming-with-anonymous-functions-part-2/
iif=@(varargin)varargin{2*find([varargin{1:2:end}],1,'first')}();
recur=@(f,varargin)f(f,varargin{:});
makeZ_2=@(x)recur(@(f,k) ...
iif(~isfield(k,'i') ...
,@()f(f,struct('i',1,'x',k ,'z',zeros(5))) ...
,(isfield(k,'i') && (k.i==1)) ...
,@()f(f,struct('i',0,'x',k.x ,'z',[k.x(1)*cos(k.x(2)) k.z(1,2:5);k.z(2:5,:)])) ...
,true ...
,@() [k.z(1:2,:);k.z(3,1:3) log(k.x(3)) k.z(3,5) ;k.z(4:5,:)] ...
),x);
makeZ_2([2 3 4])
%============= Method 3: =================================================
% built out of a recursive anonymous function as described at https://stackoverflow.com/questions/32237198/recursive-anonymous-function-matlab with ternary operator:
ternary=@(varargin)varargin{length(varargin)-varargin{1}}; ... % from https://stackoverflow.com/questions/5594937/ternary-operator-in-matlab
makeZ_3=@(makeZ_3,x) ...
feval(ternary(~isfield(x,'i') ...
,@()makeZ_3(makeZ_3,struct('i',1,'x',x ,'z',zeros(5))) ...
,@()feval(ternary(x.i==1 ...
,@()makeZ_3(makeZ_3,struct('i',0,'x',x.x ,'z',[x.x(1)*cos(x.x(2)) x.z(1,2:5);x.z(2:5,:)] )) ...
,@() [x.z(1:2,:);x.z(3,1:3) log(x.x(3)) x.z(3,5) ;x.z(4:5,:)] ))));
makeZ_3(makeZ_3,[2 3 4])
% or
mkZ_3=@(x)makeZ_3(makeZ_3,x);
mkZ_3([2 3 4])
0 comentarios
Ver también
Categorías
Más información sobre Creating and Concatenating Matrices en Help Center y File Exchange.
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!