¿Cómo ignorar elegantemente algunos valores de retorno de una función MATLAB?

120

¿Es posible obtener el valor de retorno 'enésimo' de una función sin tener que crear variables ficticias para todos los n-1valores de retorno anteriores?

Digamos que tengo la siguiente función en MATLAB:

function [a,b,c,d] = func()
a = 1;
b = 2;
c = 3;
d = 4;

Ahora suponga que solo me interesa el tercer valor de retorno. Esto se puede lograr creando una variable ficticia:

[dummy, dummy, variableThatIWillUse, dummy] = func;
clear dummy;

Pero creo que esto es algo feo . Creo que podría hacer algo como una de las siguientes cosas, pero no puede:

[_, _, variableThatIWillUse, _] = func;

[, , variableThatIWillUse, ] = func;

variableThatIWillUse = func(3);

variableThatIWillUse = func()(3);

¿Hay alguna forma elegante de hacer esto que funcione?


Hasta ahora, la mejor solución es simplemente usar variableThatIWillUsecomo variable ficticia. Esto me evita tener que crear una variable ficticia real que contamine el espacio de trabajo (o que necesitaría borrar). En resumen: la solución es usar el variableThatIWillUsepara cada valor de retorno hasta el interesante. Los valores devueltos después pueden simplemente ignorarse:

[variableThatIWillUse, variableThatIWillUse, variableThatIWillUse] = func;

Sigo pensando que este es un código muy feo, pero si no hay una mejor manera, supongo que aceptaré la respuesta.

Jordi
fuente
Además de usar una matriz de celdas como describí en mi respuesta, repetir el nombre de la variable es probablemente su única otra solución. Con suerte, los nombres de las variables no son tan largos como "variableThatIWillUse". =)
gnovice
De hecho lo son. 'tonto' fue solo un ejemplo. Normalmente usaría 'variableThatIWillNotUse'. Otras variables se denominan 'variableThatIMightUse', 'variableThatIWillUse2' y 'variableThatCanBarelyFitOnA80CharacterLine'. Estoy investigando la correlación entre nombres largos y índices de homicidio. ;)
Jordi
26
En realidad, desde R2009b, ignorar los retornos de funciones se resuelve de manera más elegante usando el '~' -Char. por ejemplo: [~, b] = sort (rand (10,1))
ymihere
1
PARA NUEVOS LECTORES: ^ debe ser la respuesta correcta. Vea la respuesta de ManWithSleeve a continuación
A.Wan
1
En su ejemplo, si solo desea el tercer argumento de salida, debe usar: [variableThatIWillUse, variableThatIWillUse, variableThatIWillUse] = func; No es necesario borrar una variable ficticia. Para las versiones más recientes de MATLAB> = R2009b, use [~, ~, variableThatIWillUse] = func;
Thierry Dalon

Respuestas:

38

Esto es algo así como un truco pero funciona:

Primero, una función de ejemplo rápido:

Func3 = @() deal(1,2,3);
[a,b,c]=Func3();
% yields a=1, b=2, c=3

Ahora, la clave aquí es que si usa una variable dos veces en el lado izquierdo de una asignación de expresión múltiple, una asignación anterior se ve afectada por la asignación posterior:

[b,b,c]=Func3();
% yields b=2, c=3

[c,c,c]=Func3();
% yields c=3

(editar: solo para verificar, también verifiqué que esta técnica funciona [mu,mu,mu]=polyfit(x,y,n)si todo lo que le importa polyfites el tercer argumento)


editar: hay un mejor enfoque; vea la respuesta de ManWithSleeve en su lugar.

Jason S
fuente
7
No había pensado en resolverlo así. Sin embargo, creo que esta solución sacrifica la claridad de la intención por la inteligencia.
Jukka Dahlbom
5
Yo personalmente solo uso [junk, junk, c] = function_call () y asumo que "junk" nunca es una variable importante y que si contiene mucha memoria, la borraré si es necesario.
Jason S
5
al votante negativo: ¿Por qué -1? Esta respuesta se escribió antes de que se lanzara R2009b, por lo que la respuesta de @ ManWithSleeve no habría funcionado en ese momento. Ahora, por supuesto, ese es el enfoque correcto.
Jason S
2
¿Quizás un comentario en la primera línea de su respuesta sería útil? Acabo de llegar aquí a través de Google, así que parece que vale la pena actualizarlo.
FvD
La asignación de izquierda a derecha no está garantizada oficialmente por The MathWorks, por lo que probablemente no debería confiar en el uso de c después de [c, c, c] = myFunc (). (Ver comentario # 26 aquí: blogs.mathworks.com/loren/2009/09/11/… )
Matt Krause
226

Con MATLAB Versión 7.9 (R2009b) puede usar un ~, por ejemplo,

[~, ~, variableThatIWillUse] = myFunction();

Tenga en cuenta que ,no es opcional. Simplemente escribir [~ ~ var]no funcionará y arrojará un error.

Consulte las notas de la versión para obtener más detalles.

ManWithSleeve
fuente
3
Es un poco molesto que no sea "_". (¿Supongo que ya se tomó?)
SamB
4
@SamB: aunque usar el notoperador como en don't caretampoco es tan malo
Tobias Kienzler
28
Tenga en cuenta que ,no es opcional. El escribir solamente [~ ~ var]lo hará sin trabajo, y generará un error.
eykanal
Yo diría que esta es la respuesta "correcta". Los otros son solo trucos para solucionar un problema que no existe. Aunque sin juego de palabras ...
patrik
6
La pregunta se planteó en 2009 antes de R2009b, momento en el que el ~ no funcionó.
Tom Anderson
37

Si desea utilizar un estilo en el que se dejará que una variable caiga en el cubo de bits, una alternativa razonable es

[ans,ans,variableThatIWillUse] = myfun(inputs);

ans es, por supuesto, la variable basura predeterminada para matlab, y se sobrescribe a menudo en el transcurso de una sesión.

Si bien me gusta el nuevo truco que ahora permite MATLAB, el uso de ~ para designar una variable de retorno ignorada, es un problema de compatibilidad con versiones anteriores, ya que los usuarios de versiones anteriores no podrán usar su código. Por lo general, evito usar cosas nuevas como esa hasta que se hayan publicado al menos algunas versiones de MATLAB para garantizar que queden muy pocos usuarios en la estacada. Por ejemplo, incluso ahora encuentro que la gente sigue usando una versión de MATLAB lo suficientemente antigua como para no poder usar funciones anónimas.


fuente
7
Sí, es inteligente, pero el editor nativo de Matlab dará una advertencia si asigna algo a la variable ans. No creo que tener avisos sea muy elegante ...
Jordi
11
Puede desactivar la advertencia. Termine la línea con esta cadena de comentario% # ok Mlint lo ignorará. Sin advertencias.
13

Aquí tienes otra opción que puedes usar. Primero haga una matriz de celdas para capturar todas las salidas (puede usar la función NARGOUT para determinar cuántas salidas devuelve una función dada):

a = cell(1,3);  % For capturing 3 outputs
% OR...
a = cell(1,nargout(@func));  % For capturing all outputs from "func"

Luego llame a la función de la siguiente manera:

[a{:}] = func();

Luego, simplemente elimine el elemento de a que desee y sobrescriba a :

a = a{3};  % Get the third output
gnovice
fuente
9

Escribí una función kth out:


function kth = kthout(k,ffnc,varargin)
%% kthout: take the kth varargout from a func call %FOLDUP
% 
% kth = kthout(k,ffnc,varargin)
%
% input:
%  k                      which varargout to get
%  ffnc                   function to call;
%  varargin               passed to ffnc;
% output:
%  kth                    the kth argout;
% global:
% nb: 
% See also:
% todo:
% changelog: 
%
%% %UNFOLD

[outargs{1:k}]  = feval(ffnc,varargin{:});
kth                         = outargs{k};

end %function

entonces puedes llamar

val_i_want  = kthout(3,@myfunc,func_input_1,func_input_2); %etc

también puede terminar la función como

func_i_want = @(varargin)(kthout(3,@myfunc,varargin{:}));  %assuming you want the 3rd output.

después de lo cual usas

val_i_want = func_i_want(func_input_1,func_input_2);

tenga en cuenta que hay una sobrecarga asociada con el uso de funciones anónimas como esta, y esto no es algo que haría en un código que se llamaría miles de veces.

shabbychef
fuente
4

En Matlab 2010a, encontré una forma elegante de hacer lo que está pidiendo. Es simplemente usar el carácter "~" (sin las comillas, por supuesto) como su variable ficticia (tantas como desee al devolver múltiples parámetros). Esto también funciona para los parámetros de entrada a las funciones si las funciones están diseñadas para manejar datos faltantes. No sé si esto existía en versiones anteriores, pero me lo encontré recientemente.

Sam
fuente
11
¿No viste la respuesta anterior?
yuk
1

Puede crear una función (o función anónima) que solo devuelva salidas seleccionadas, por ejemplo

select = @(a,b) a(b);

Entonces puedes llamar a tu función así:

select(func,2);
select(func,1:3);

O puede asignar la salida a una variable:

output(1,2:4) = select(func,1:3);
Dave
fuente
no funciona para mí. Probadodecimatedfftx = select(fft(x,12),1:4:12);
NotGaeL
1
select(func,2)llamadas func(2). No veo dónde esto selecciona argumentos de salida.
Cris Luengo
0

¿Hay alguna razón para no usar ans (n), como esta:

a=rand([5 10 20 40]);

size(a);

b=ans(2);

Da b = 10, ¿y de esta manera no sería compatible con todas las versiones de Matlab?

Además, esto funciona para obtener el segundo argumento de salida cuando no se sabe cuántos argumentos habrá. Considerando que, si haces esto:

[~, b] = size(a);

¡Entonces b = 8000! (¡Tienes que terminar con ~ para captar más argumentos!)

usuario1596274
fuente
Esta respuesta supone que la variable que se devuelve es un vector, que probablemente no era lo que significaba el OP.
Neil Traft
Esto no tiene sentido. size(a)y [b,c]=size(a)devolver cosas diferentes. Las funciones de MATLAB cambian el comportamiento en función del número de argumentos de salida.
Cris Luengo
Me cuesta entender esta respuesta. No sé cómo esto contribuye a la calidad de las respuestas aquí, y mucho menos si esto no responde directamente a la pregunta original.
rayryeng
Han pasado 6 años y ya no uso Matlab. Por lo que recuerdo, la función "tamaño ()" era irrelevante; solo la usé como una función que devolvería múltiples argumentos. El punto es que simplemente puedo llamar a func () y luego ans (n) para obtener el valor de la variable devuelta número n. Esto pareció funcionar bien para ciertas situaciones y ser compatible con versiones anteriores. Puede que solo funcione con ciertas funciones, por supuesto, o tipos de variables, lo que sea. Eso es todo lo que puedo ayudar 6 años después.
user1596274