Is it possible to write several statements into an anonymous function?

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 Comments

Why are you so limited by disk space? You realize that MATLAB M-files are just text files, right? They are about as small a file as you can hope for in today's environment.
I don't know about metaprogramming, but anonymous functions are generally slower than M-files. Also, the only way to do what the OP wants to do (yes, it is possible) would be even slower.
@Matt sounds like a new puzzler ...
Hopefully, Patrik sees advantages that we don't.
I read about metaprogramming through the link that Per provided. It seems to be a potential solution to the problem. Below I suggested creating a different anonymous function and it seems that this is in line with the idea of metaprogramming.
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...

Sign in to comment.

Answers (11)

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)))
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:
  1. 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.
  2. 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 Comments

I don't think your nop works correctly for functions that are defined as returning no outputs. The example you give is
@(s) nop(s, fprintf('hello world\n')), ...
but fprintf() does return an output that can then be dropped into the second input of the function and ignored there. There is a harder problem for cases where outputs are not permitted. Consider for example,
nop('hello', set(0,'visible','on'))
ideally this would return just the 'hello', but instead it errors
One or more output arguments not assigned during call to "set".
We need a construct that can take functions that cannot return output, and turn those into calls that return the empty array. At the moment it seems to me that such calls are mostly made for their side effects, which is not good clear lambda calculus... but the calls are important anyhow.
You are right, nop does not have its intended effect. The entire point was to call functions that return no outputs, and it fails at doing that. I don't know of a way to do this.
I suppose those operations would have to be wrapped in a local function that returns at least one dummy result, unless some mechanism can be found to invoke them from an expression.
It can be done with evalc()... or let me see
xyxy = @() set(gcf, 'visible','on');
pqr = feval(xyxy)
pqr =
[]
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'.

Sign in to comment.

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 Comments

Hi, The advantage of anonymous functions is that you do not have to write them to disk, which I would like to avoid as much as possible. Your solution would work for the current example but does not seem easily generalizable.
Why do you want to avoid writing to disk?
You mean you want to avoid writing extra files? I can't see why.
In any case, a generalization of anonymous functions that allows multiple lines/commands does exist. It's called a nested function, e.g.,
function YourFunction
%Other code here
function z= make5x5(x) %nested function
z=zeros(5);
z(1,1)=x(1)*cos(x(2));
z(3,4)=log(x(3));
end
end
He didn't believe us when he asked in Feb that this is a bad strategy and he is apparently at it again.
Daniel, I still don't believe it is a bad strategy in all circumstances. Suppose you want to compare, say, 20 different specifications of a huge system. Writing files to disk would imply reparsing each specification and writing a separate file in each case. Instead, in my framework I can just move to a different specification by issuing a statement that says in what respects the current specification differs from an earlier one. I can do it without knowing in advance how many specifications I will be dealing with.
Besides, the problem here is different and in fact simpler than what you are referring to.
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?

Sign in to comment.

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)));
I asked this question 5 years ago and it is still active today. I had almost forgotten that I did ask such a question, which led me to ask variants of the same questions. My sense is that this is an important problem.
The statements composing the function can have if/else statements, there could be intermediary terms but no for or while loops.
The quickest and dirtiest thing to do is obvious. One can simply write all the statements as a string and evaluate them. But most people would argue against using eval. Eval has drawbacks for sure but clarity need not be one of them. It is always possible to evaluate a string inside a dedicated function to avoid messing up a particular workspace.
I very much appreciate all the proposals that have been suggested. One of the main messages I get from them is that what would be otherwise a simple function should be turned into a difficult-to-read set of statements if one tries to create an anonymous function.
My guess is that functions like these should be straightforward to handle in other languages. In any case, for me, this is so far the most challenging programming task I have faced in Matlab and it seems it was already an issue long before I asked: https://www.mathworks.com/matlabcentral/newsreader/view_thread/143778
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]));
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.
I have a solution to my own question but I don't know whether it is the best strategy: Create an anonymous function that is more general than matlab's anonymous function.
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 Comments

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.
My solution was specifically coded to avoid the reshape().

Sign in to comment.

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)');
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])

Categories

Find more on Function Creation in Help Center and File Exchange

Asked:

on 8 Oct 2012

Answered:

on 1 Dec 2023

Community Treasure Hunt

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

Start Hunting!