How to pass multiple arguments to a function handle?

17 Ansichten (letzte 30 Tage)
泰麟
泰麟 am 21 Okt. 2024
Kommentiert: Steven Lord am 21 Okt. 2024
I try to define a function named myfun(f, varargin) to evaluate a general multivariate function , the first parameter f is a function handle (avoid using 'sym' objects and subs() for their low performance) while the parameters varargin are supposed to be values of .
Here comes the problem: I want this myfun() to evaluate the function f for arbitary integer n, i.e. it should work on functions with different independent variable number n, but I fail to pack all the input values of and pass them together to a function handle .
It seems that there're no elegent ways to pack and pass multiple arguments to a function handle, unlike Python I may just write code like
myfun(f, *x_tuple)
where x_tuple is a n-length tuple of values coresponding to each . In python the function myfun() will automatically read all values in x_tuple and pass them to corresponding parameters of myfun(). Can we do similar things in MATLAB?
I also figure out a way to solve this problem using eval() : (I rewrite the function as myfun(f, x_values))
(i) Get the length of x_values (it's supposed to be a vector), that is, the number of independent variables n;
(ii) Loop over indices and concatenate the strings like
mystr = 'f(';
for i = 1:n
if i < n
mystr = [mystr, num2str(x_values(i)), ','];
else
mystr = [mystr, num2str(x_values(i))];
end
end
mystr = [mystr, ')'];
(iii) Evaluate mystr via eval() I get the value I want.
But using eval() is usually not a good idea because of its inefficiency and insecurity. Are there any better methods for the above problem?

Antworten (2)

Star Strider
Star Strider am 21 Okt. 2024
An example of what you want to do could help.
Perhaps something like this —
for k = 1:10
k
arg = randn(1,k)
out = myfun(arg)
end
k = 1
arg = -0.6597
out = 1×5
-0.6597 -0.6597 -0.6597 -0.6597 0
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
k = 2
arg = 1×2
0.3100 1.2764
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
out = 1×5
0.3100 1.2764 0.7932 0.7932 0.6833
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
k = 3
arg = 1×3
-0.4079 -1.0190 -0.0317
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
out = 1×5
-1.0190 -0.0317 -0.4862 -0.4079 0.4983
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
k = 4
arg = 1×4
-1.2197 -0.1319 0.1128 -0.4728
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
out = 1×5
-1.2197 0.1128 -0.4279 -0.3024 0.5799
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
k = 5
arg = 1×5
1.8721 -0.1714 0.7697 -0.9548 0.5798
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
out = 1×5
-0.9548 1.8721 0.4191 0.5798 1.0602
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
k = 6
arg = 1×6
0.6598 1.1099 -1.4821 -0.6736 -0.0791 -0.1868
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
out = 1×5
-1.4821 1.1099 -0.1086 -0.1330 0.9261
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
k = 7
arg = 1×7
1.4194 -2.3117 0.0009 -0.2894 1.4609 -0.2112 -0.5642
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
out = 1×5
-2.3117 1.4609 -0.0708 -0.2112 1.2848
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
k = 8
arg = 1×8
1.2758 0.7314 -1.1727 -0.1510 0.4838 0.2013 -0.7007 -1.8769
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
out = 1×5
-1.8769 1.2758 -0.1511 0.0252 1.0465
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
k = 9
arg = 1×9
0.1018 0.0019 0.8604 -1.3725 0.1011 0.9371 0.8539 1.6752 1.1337
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
out = 1×5
-1.3725 1.6752 0.4770 0.8539 0.8871
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
k = 10
arg = 1×10
0.6290 -0.7779 -1.2671 0.4628 -0.3292 -1.2370 -2.0449 0.0383 -0.8039 0.7392
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
out = 1×5
-2.0449 0.7392 -0.4591 -0.5535 0.9266
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
function out = myfun(varargin)
xvecc(1:numel(varargin)) = varargin;
xvec = cell2mat(xvecc);
out = [min(xvec) max(xvec) mean(xvec) median(xvec) std(xvec)];
end
I am not certain what problem you may be having.
This is the approach I usually use witth varargin arguments.
.
  3 Kommentare
Star Strider
Star Strider am 21 Okt. 2024
My pleasure!
But what if I want to evaluate fn = x_1 .^ x_2 .^ x_3 .^ ... .^ x_n with x_i = i/10 ?
That can be vectorised using MATLAB’s vectorisation syntax and calls to log and exp.
Example —.
x_i = [0.1 rand(1,99)];
disp(x_i)
Columns 1 through 18 0.1000 0.5772 0.1040 0.1087 0.6084 0.3475 0.0374 0.5987 0.8615 0.6141 0.1742 0.9081 0.2084 0.8300 0.4367 0.7336 0.7269 0.7439 Columns 19 through 36 0.3182 0.3563 0.1456 0.3318 0.4131 0.7964 0.7546 0.1851 0.2555 0.1270 0.7008 0.9272 0.3265 0.7042 0.4732 0.9151 0.6546 0.8541 Columns 37 through 54 0.0325 0.9272 0.8212 0.0335 0.3454 0.5973 0.4389 0.7381 0.4744 0.3114 0.0529 0.3957 0.8903 0.1127 0.1362 0.4039 0.8269 0.4988 Columns 55 through 72 0.4667 0.0540 0.1926 0.7464 0.1084 0.1815 0.6991 0.5170 0.8582 0.9098 0.7592 0.1426 0.6971 0.9879 0.4894 0.7612 0.9992 0.0144 Columns 73 through 90 0.3382 0.6090 0.7617 0.2211 0.8165 0.7238 0.7964 0.7837 0.1239 0.0706 0.5597 0.6769 0.1094 0.9350 0.8733 0.3548 0.6657 0.7137 Columns 91 through 100 0.9878 0.6806 0.9367 0.2955 0.8320 0.6085 0.6739 0.1522 0.7989 0.5597
tic
x_e(1) = x_i(1);
for k = 1:numel(x_i)-1
x_e(k+1) = x_e(k)^x_i(k+1);
end
toc
Elapsed time is 0.004088 seconds.
disp(x_e)
Columns 1 through 18 0.1000 0.2647 0.8709 0.9851 0.9909 0.9968 0.9999 0.9999 0.9999 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 Columns 19 through 36 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 Columns 37 through 54 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 Columns 55 through 72 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 Columns 73 through 90 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 Columns 91 through 100 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000
tic
x_ee = [x_e(1) exp(log(x_i(1))*cumprod(x_i(2:end)))];
toc
Elapsed time is 0.001515 seconds.
disp(x_ee)
Columns 1 through 18 0.1000 0.2647 0.8709 0.9851 0.9909 0.9968 0.9999 0.9999 0.9999 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 Columns 19 through 36 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 Columns 37 through 54 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 Columns 55 through 72 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 Columns 73 through 90 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 Columns 91 through 100 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000
The vectorisation approach even appears to be a bit faster
I am not certain that I understand what you want to do. I doubt that there is a general approach that could apply to any arbitrary function of a vector argument that has a large (and possibly memory-saturating) number of elements.
With respect to using varargin howeveer, my approach should work.
.
Steven Lord
Steven Lord am 21 Okt. 2024
But what if I want to evaluate fn = x_1 .^ x_2 .^ x_3 .^ ... .^ x_n with x_i = i/10 ?
When n is large (namely, 10^4 or even larger),
There is a limit on the maximum number of input arguments you can specify for a function in MATLAB. I don't remember what the value is offhand, but if you're trying to pass ten thousand separate inputs to your function you may reach that limit.
Even if you don't reach that limit, if your number of input arguments gets into double digits your function is probably too unwieldy to use especially if some have a specific meaning depending on where in the argument list they appear. [fmincon from Optimization Toolbox is one example of a function that is harder to use than it should be because it has ten positional input arguments and it's way too easy to miss one. We've seen plenty of complaints on Answers about error messages caused by missing one.]
Don't pass the inputs in as separate values. Pass in one input argument that is an array. For the specific example you specified:
y = powertower((1:5)/10)
y = 0.9727
function y = powertower(x)
if ~isempty(x)
y = x(1); % Assuming you want (x(1)^x(2))^x(3) not x(1)^(x(2)^x(3))
for k = 2:numel(x)
y = y.^x(k);
end
else
y = NaN; % Placeholder
end
end

Melden Sie sich an, um zu kommentieren.


Saurabh
Saurabh am 21 Okt. 2024
Bearbeitet: Saurabh am 21 Okt. 2024
Hi @泰麟,
I understand that you want to define a function to evaluate a general multivariable function.
So let assume that the function 'f' calculates the sum of the squares of input arguments.
function result = f(varargin)
% f calculates the sum of the squares of input arguments.
% varargin allows a variable number of inputs.
% Initialize the result
result = 0;
% Loop through each input argument
for k = 1:length(varargin)
% Ensure the input is numeric
if isnumeric(varargin{k})
% Add the square of the current argument to the result
result = result + varargin{k}^2;
else
error('All inputs must be numeric.');
end
end
end
'myfun' Function: This function takes the function handle and the variable inputs, unpacks them, and evaluates the function.
function result = myfun(f, varargin)
% f is a function handle
% varargin is a cell array containing the input values
% Call the function handle with the unpacked arguments
result = f(varargin{:});
end
Function Handle: When calling 'myfun', we use @f to pass the function handle of f.
% Example usage with different numbers of arguments
result1 = myfun(@f, 1, 2, 3); % Evaluates f(1, 2, 3)
disp(['Result 1: ', num2str(result1)]);
result2 = myfun(@f, 4, 5); % Evaluates f(4, 5)
disp(['Result 2: ', num2str(result2)]);
result3 = myfun(@f, 6); % Evaluates f(6)
disp(['Result 3: ', num2str(result3)]);
Refer to the following link to know more about the 'varargin' function:
I hope this was helpful.
  1 Kommentar
泰麟
泰麟 am 21 Okt. 2024
Much thanks for your answer!
In fact, my question is to go one step further than your answer:
Now consider a function handle f with 10000 arguments, but we don't know what its expression is, all we know is that if we pass 10000 values to its corresponding arguments, it will output the value we want.
Now pass 1,...,10000 to f to evaluate it. We can just run the codes like:
f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ... , 10000)
but the codes are too long that we want them to be less complicated, maybe like:
f(1:10000) or sth simpler
Note that we don't know the math expression of f, so we can't evaluate it by parts using a for-loop.
In this situation, can we make it?

Melden Sie sich an, um zu kommentieren.

Tags

Produkte


Version

R2024a

Community Treasure Hunt

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

Start Hunting!

Translated by