6

I have a long anonymous function, and I wondered whether the help output can be modified (easily):

>> myfunc=@(x) x; %example anonymous function
>> help myfunc
myfunc is a variable of type function_handle.

I know long anonymous functions might be a rather unusual thing- nevertheless: can this be achieved, maybe with undocumented functions, for only as long as the function handle exists?

Edit: commenter asked for a use-case: I read up on anoynmous functions with muiltiple outputs (here Lorem on the art of matlab) such as

fmeanVar = @(x) deal(mean(x), var(x));
%a long example function to split a cell array containing 3D matrices into two cell arrays 
myfunc=@(x,c) deal(cellfun(@(d) d(:,:,1:c:end),x),cellfun(@(d) d(:,:,setxor(1:c:end,1:end)),x));

And I want to make sure I remember what the second output argument is, later in time, you know... because humans forget stuff

user2305193
  • 2,115
  • 14
  • 34
  • 1
    Any particular reason for not using a conventional function which lets you write that? – Sardar Usama Dec 10 '18 at 13:59
  • 2
    Interesting question. I don’t think this is possible. Could you share your use case for this? – Cris Luengo Dec 10 '18 at 14:09
  • @SardarUsama: no particular reason, except a dislike to have an extra files for smaller functions – user2305193 Dec 10 '18 at 14:12
  • 4
    I like Nicky’s idea as a workaround. But if you do this just to avoid lots of small files, maybe you don’t realize that you can define private functions within an M-file (either function M-file of script). Anonymous functions (or function lambdas in other languages) are typically only used locally, mostly to pass as the argument to another function. This is the reason they don’t carry documentation. Local usage means you can read the comments where it is defined. – Cris Luengo Dec 10 '18 at 14:30
  • 1
    As @CrisLuengo said you can have multiple [local functions](https://www.mathworks.com/help/matlab/matlab_prog/local-functions.html) in a single file. – rahnema1 Dec 10 '18 at 14:31
  • @CrisLuengo I think function support within a script comes only with newer Matlab versions which caused troubles before and made me look for alternatives. But agreed, that would be perfect. Maybe this is just me but - generally - writing nested functions turned out to be less straightforward to debug (jumping in and out of functions etc) compared to running a script and having every important variable in workspace. On the other hand, debugging anonymous functions is even less straightforward, but I usually don't because it's still a relatively 'simple' function – user2305193 Dec 10 '18 at 14:44
  • 1
    @user2305193 If you think the MATLAB version you're using will influence the answer, please specify it in the question :) – Wolfie Dec 11 '18 at 15:11

3 Answers3

6

The problem is that when you invoke help it re-reads the file. When you create an anonymous function with

f = @(x) x %Sample text

then it ignores %Sample text and is thus gone. One solution is to turn it into a struct, where one field is the function and the other the help. E.g. something like

fMeanVar.fct = @(x) [mean(x), var(x)];
fMeanVar.help = "Second output is the variance"

thus when you want to use the function you call

fMeanVar.fct([1,2,3,4])

and if you forget the usage you can simply call

fMeanVar.help
Nicky Mattsson
  • 3,032
  • 9
  • 26
  • You can use this approach but make it mimic native function syntax more closely if you [make a class](https://stackoverflow.com/a/53709764/39785450) – Wolfie Dec 10 '18 at 16:45
  • @Wolfie I did consider to do that, but found it to cumbersome for the time I had on my hands(and how well I program classes). I am glad to see that you went all in and actually did it! – Nicky Mattsson Dec 10 '18 at 18:49
6

You can create your own anonymous function handling class which would mimic this functionality, shadowing the help function for this object type only.

I've written the class below, but will show usage first, it simply requires having the class on your path and slightly adapting the way you declare anonymous functions:

We can override the subsref function for this class type also, then you can call the function handle directly using () syntax, rather than indexing into a structure as suggested by Nicky's answer.

Note that you have to pass the handle in, not the function name (i.e. help(f) or f.help, not help f or help('f')). You'd have to fully shadow the help function to get around this limitation, which I wouldn't really endorse!

Usage

>> f = anon( @() disp( 'Hi!' ), 'This function displays "Hi!"' );
>> help( f )
Input is a value of type function_handle.
This function displays "Hi!"
>> f()
Hi!

>> f = anon( @(x) x + 10, 'Adds 10 to the input' );
>> help( f )
Input is a value of type function_handle.
Adds 10 to the input
>> f(15:17)
ans = 
  [ 25, 26, 27 ]

>> f.func = @(x) x + 15;
>> f.helpStr = 'Adds 15 to the input'
>> f(15:17)
ans = 
  [ 30 31 32 ]

Default function handle help is preserved if not specified

>> f = anon( @(x) x + 10 );
>> help( f )
Input is a value of type function_handle.

Class code

The class could use some extra input checking etc, but works in principle!

classdef anon < handle
    properties ( Access = public )
        helpStr % String to display on help( obj )
        func    % Function handle (meant for anonymouse functions
    end
    methods
        function obj = anon( func, helpStr )
            assert( isa( func, 'function_handle' ) ); % Input check            
            obj.func = func;
            if nargin > 1
                obj.helpStr = helpStr; % Set help string
            end
        end
        function help( obj )
            h = help( obj.func ); % Normal behaviour.
            if ~isempty( obj.helpStr )
                % Custom string (does nothing if empty)
                fprintf( '%s%s\n', h, obj.helpStr );   
            else
                disp( h );
            end
        end
        function varargout = subsref( obj, s )
            % Need to override the subsref behaviour to enable default
            % function calling behaviour!
            switch s(1).type
                case '()'
                    [varargout{1:nargout}]  = obj.func( s(1).subs{:} );
                otherwise
                    [varargout{1:nargout}]  = builtin('subsref', obj, s);
            end
        end
    end
end
Cris Luengo
  • 43,236
  • 6
  • 46
  • 92
Wolfie
  • 21,886
  • 7
  • 23
  • 50
2

According to Matlab documentation for help, it's not possible:

help name displays the help text for the functionality specified by name, such as a function, method, class, toolbox, or variable.

Not for handle nor symbolic.

alpereira7
  • 1,444
  • 3
  • 18
  • 28