Jak elegancko zignorować niektóre wartości zwracane przez funkcję MATLAB?

120

Czy możliwe jest pobranie wartości zwracanej przez „n-tą” z funkcji bez konieczności tworzenia zmiennych fikcyjnych dla wszystkich n-1wartości zwracanych przed nią?

Powiedzmy, że mam następującą funkcję w MATLAB:

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

Załóżmy teraz, że interesuje mnie tylko trzecia zwracana wartość. Można to osiągnąć, tworząc jedną fikcyjną zmienną:

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

Ale myślę, że to trochę brzydkie . Myślę, że możesz zrobić coś podobnego do jednej z następujących rzeczy, ale nie możesz:

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

[, , variableThatIWillUse, ] = func;

variableThatIWillUse = func(3);

variableThatIWillUse = func()(3);

Czy są na to eleganckie sposoby, które działają?


Jak dotąd najlepszym rozwiązaniem jest po prostu użycie variableThatIWillUsezmiennej zastępczej. Dzięki temu nie muszę tworzyć prawdziwej fikcyjnej zmiennej, która zanieczyszcza przestrzeń roboczą (lub którą musiałbym wyczyścić). W skrócie: rozwiązaniem jest użycie variableThatIWillUsedla każdej zwracanej wartości aż do interesującej. Wartości zwracane po można po prostu zignorować:

[variableThatIWillUse, variableThatIWillUse, variableThatIWillUse] = func;

Nadal uważam, że to bardzo brzydki kod, ale jeśli nie ma lepszego sposobu, to chyba zaakceptuję odpowiedź.

Jordi
źródło
Oprócz korzystania z tablicy komórek, jak opisałem w mojej odpowiedzi, powtórzenie nazwy zmiennej jest prawdopodobnie jedynym innym rozwiązaniem. Miejmy nadzieję, że nazwy twoich zmiennych nie są tak długie, jak „variableThatIWillUse”. =)
gnovice
Właściwie to są. „manekin” był tylko przykładem. Normalnie użyłbym „variableThatIWillNotUse”. Inne zmienne to „variableThatIMightUse”, „variableThatIWillUse2” i „variableThatCanBarelyFitOnA80CharacterLine”. Badam korelację między długimi nazwiskami a wskaźnikami zabójstw. ;)
Jordi
26
Właściwie od wersji 2009b ignorowanie zwrotów funkcji jest rozwiązywane bardziej elegancko za pomocą '~' -Char. np .: [~, b] = sort (rand (10,1))
ymihere
1
DLA NOWYCH CZYTELNIKÓW: ^ powinno być poprawną odpowiedzią. Zobacz odpowiedź ManWithSleeve poniżej
A.Wan
1
W twoim przykładzie, jeśli chcesz tylko trzeciego argumentu wyjściowego, powinieneś użyć: [variableThatIWillUse, variableThatIWillUse, variableThatIWillUse] = func; Nie ma potrzeby czyszczenia fikcyjnej zmiennej. W przypadku nowszych wersji MATLAB> = R2009b użyj [~, ~, variableThatIWillUse] = func;
Thierry Dalon

Odpowiedzi:

38

To trochę hack, ale działa:

Najpierw krótka przykładowa funkcja:

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

Kluczem tutaj jest to, że jeśli użyjesz zmiennej dwukrotnie po lewej stronie przypisania z wieloma wyrażeniami, wcześniejsze przypisanie zostanie zablokowane przez późniejsze przypisanie:

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

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

(edytuj: żeby sprawdzić, zweryfikowałem również, że ta technika działa, [mu,mu,mu]=polyfit(x,y,n)jeśli wszystko, na czym ci zależy, polyfitto trzeci argument)


edycja: jest lepsze podejście; zamiast tego zobacz odpowiedź ManWithSleeve .

Jason S.
źródło
7
Nie myślałem o rozwiązaniu tego w ten sposób. Uważam jednak, że to rozwiązanie poświęca jasność intencji na rzecz sprytu.
Jukka Dahlbom
5
Osobiście po prostu używam [junk, junk, c] = function_call () i zakładam, że „śmieci” nigdy nie jest ważną zmienną i jeśli zawiera dużo pamięci, to wyczyszczę ją, jeśli będzie to konieczne.
Jason S,
5
do przeciwnika: dlaczego -1? Ta odpowiedź została napisana jeszcze przed wydaniem R2009b, więc odpowiedź @ ManWithSleeve nie zadziałałaby wtedy. Oczywiście to właściwe podejście.
Jason S
2
Może komentarz w pierwszej linii Twojej odpowiedzi byłby pomocny? Właśnie przyszedłem tutaj przez Google, więc wydaje się, że warto zaktualizować.
FvD
Przypisanie od lewej do prawej nie jest oficjalnie gwarantowane przez The MathWorks, więc prawdopodobnie nie powinieneś polegać na używaniu c po [c, c, c] = myFunc (). (Zobacz komentarz nr 26 tutaj: blogs.mathworks.com/loren/2009/09/11/… )
Matt Krause
226

W MATLAB w wersji 7.9 (R2009b) możesz użyć ~, np.

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

Zauważ, że ,nie jest opcjonalny. Samo wpisanie [~ ~ var]nie zadziała i spowoduje wyświetlenie błędu.

Zobacz informacje o wersji, aby uzyskać szczegółowe informacje.

ManWithSleeve
źródło
3
Trochę irytujące, że to nie jest „_”. (Przypuszczam, że to już zostało zrobione?)
SamB
4
@SamB: chociaż używanie notoperatora jako don't caretakiego też nie jest takie złe
Tobias Kienzler,
28
Zwróć uwagę, że ,nie jest opcjonalne. Wpisanie [~ ~ var]woli nie pracować, i wygeneruje błąd.
eykanal
Powiedziałbym, że to jest „poprawna” odpowiedź. Inne to tylko hacki, które rozwiązują problem, który nie istnieje. Jednak żadna gra słów nie była zamierzona ...
patrik
6
Pytanie zostało postawione w 2009 roku przed rokiem 2009b, kiedy to ~ nie działało.
Tom Anderson
37

Jeśli chcesz użyć stylu, w którym zmienna zostanie pozostawiona do segmentu bitów, rozsądną alternatywą jest

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

ans jest oczywiście domyślną zmienną śmieciową dla Matlaba, często nadpisywaną w trakcie sesji.

Chociaż podoba mi się nowa sztuczka, na którą teraz pozwala MATLAB, użycie ~ do oznaczenia ignorowanej zmiennej zwrotnej, jest to problem ze wsteczną kompatybilnością, ponieważ użytkownicy starszych wersji nie będą mogli używać twojego kodu. Generalnie unikam używania takich nowych rzeczy, dopóki co najmniej kilka wydań MATLAB-a nie zostanie wydanych, aby zapewnić, że pozostanie bardzo niewielu użytkowników. Na przykład nawet teraz stwierdzam, że ludzie nadal używają wystarczająco starej wersji MATLAB-a, że ​​nie mogą używać funkcji anonimowych.


źródło
7
Tak, to sprytne, ale natywny edytor Matlab da ostrzeżenie, jeśli przypiszesz cokolwiek do zmiennej ans. Nie sądzę, żeby ostrzeżenia były bardzo eleganckie ...
Jordi
11
Możesz wyłączyć ostrzeżenie. Zakończ linię tym komentarzem% # ok Mlint zignoruje to. Brak ostrzeżeń.
13

Oto inna opcja, której możesz użyć. Najpierw utwórz tablicę komórek, aby przechwycić wszystkie wyjścia (możesz użyć funkcji NARGOUT, aby określić, ile wyjść zwraca dana funkcja):

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

Następnie wywołaj funkcję w następujący sposób:

[a{:}] = func();

Wtedy po prostu usunąć element z że chcesz, i nadpisać :

a = a{3};  % Get the third output
gnovice
źródło
9

Napisałem k-tą funkcję:


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

możesz wtedy zadzwonić

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

możesz również zawinąć funkcję, taką jak

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

po którym używasz

val_i_want = func_i_want(func_input_1,func_input_2);

zwróć uwagę, że korzystanie z takich anonimowych funkcji wiąże się z narzutem i nie jest to coś, co robiłbym w kodzie, który byłby wywoływany tysiące razy.

shabbychef
źródło
4

W Matlab 2010a znalazłem zgrabny sposób na robienie tego, o co prosisz. Wystarczy użyć znaku "~" (oczywiście bez cudzysłowów) jako zmiennej fikcyjnej (tyle, ile chcesz, gdy zwracasz wiele parametrów). Działa to również w przypadku parametrów wejściowych do funkcji, jeśli funkcje są zaprojektowane do obsługi brakujących danych. Nie wiem, czy istniało to w poprzednich wersjach, ale niedawno się z tym spotkałem.

Sam
źródło
11
Nie widziałeś poprzedniej odpowiedzi?
fuj
1

Możesz utworzyć funkcję (lub funkcję anonimową), która zwraca tylko wybrane wyniki, np

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

Następnie możesz wywołać swoją funkcję w ten sposób:

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

Możesz też przypisać dane wyjściowe do zmiennej:

output(1,2:4) = select(func,1:3);
Dave
źródło
nie działa dla mnie. Wypróbowanydecimatedfftx = select(fft(x,12),1:4:12);
NotGaeL
1
select(func,2)wezwania func(2). Nie widzę, gdzie to wybiera argumenty wyjściowe.
Cris Luengo,
0

Czy jest jakiś powód, aby nie używać ans (n), takich jak ten:

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

size(a);

b=ans(2);

Daje b = 10 i czy w ten sposób nie byłoby kompatybilne ze wszystkimi wersjami Matlab?

Ponadto działa to w celu uzyskania drugiego argumentu wyjściowego, gdy nie wiesz, ile będzie argumentów! A jeśli to zrobisz:

[~, b] = size(a);

Wtedy b = 8000! (Musisz zakończyć ~, aby złapać więcej argumentów!)

user1596274
źródło
Ta odpowiedź zakłada, że ​​zwracana zmienna jest wektorem, co prawdopodobnie nie było tym, co oznaczał OP.
Neil Traft
To nie ma sensu. size(a)i [b,c]=size(a)zwracaj różne rzeczy. Funkcje w MATLAB-ie zmieniają zachowanie na podstawie liczby argumentów wyjściowych.
Cris Luengo
Trudno mi zrozumieć tę odpowiedź. Nie wiem, jak to wpływa na jakość odpowiedzi tutaj, nie mówiąc już o tym, że nie odpowiada to bezpośrednio na pierwotne pytanie.
rayryeng,
Minęło 6 lat i nie używam już Matlab. O ile dobrze pamiętam, funkcja „size ()” była nieistotna - po prostu użyłem jej jako funkcji zwracającej wiele argumentów. Chodzi o to, że mogę po prostu wywołać func (), a następnie ans (n), aby uzyskać wartość zwróconej zmiennej o numerze n. Wydawało się, że działa to dobrze w niektórych sytuacjach i jest kompatybilne wstecz. Może oczywiście działać tylko z pewnymi funkcjami lub typami zmiennych, cokolwiek. To tyle, ile mogę pomóc 6 lat później.
user1596274