W jaki sposób ulepszyć samodzielnie pisane funkcje w MATLABie? Jak łatwo zaimplementować obsługę błędów związaną z przekazywanymi do funkcji argumentami (zmiennymi)? W tym wpisie zajmę się względnie nową funkcjonalnością MATLABa czyli walidacją argumentów funkcji za pomocą struktury arguments .. end.
Tworząc własną funkcję, realizującą dane obliczenia, warto zadbać o obsługę błędów, która jest związana z przyjmowanymi przez funkcję argumentami. Jeżeli na przykład rozmiar lub typ przekazanego do funkcji argumentu (zmiennej) nie jest zgodny z zamysłem twórcy funkcji, może to spowodować błąd i przerwanie programu bez szczegółowego komunikatu o przyczynie. Taka sytuacja może mieć miejsce kiedy argument funkcji powinien być skalarem, a przekazujemy wektor lub kiedy zmienna powinna być typu numerycznego, a przekazujemy tekst, itp.
Dlatego dobrą praktyką jest aby każda funkcja posiadała obsługę błędów i jednocześnie szczegółowo komunikowała programiście o przyczynie takiej sytuacji. Do niedawna w MATLABie do tego celu używało się zestawu warunków if.. elseif.. end oraz funkcji nargin, nargout, isnumeric i innych. Powodowało to, że fragment funkcji związany z obsługą błędów argumentów wejściowych był długi i nieczytelny. Weźmy poniższy przykład. Mamy prosty program, zawierający funkcję, która realizuje mnożenie zmiennych xin i yin przez wartość zdefiniowaną w zmiennej param.
clear all; close all; clc x = [0 1 2 3 4]; y = [10 11 12 13 14]; param = 2; [X,Y] = myfunc(x,y,param) %% function [xout,yout] = myfunc(xin,yin,param) % myfunc .... % Obsługa błędów if nargin < 2 error('za mało argumentów wejściowych') end if nargin > 2 if ~isnumeric(xin) || ~isnumeric(yin) error('x oraz y muszą być liczbami') end end if nargin == 2 param = 2; end if nargin == 3 && (~isscalar(param) || ~isnumeric(param)) error('flag musi być skalarem i liczbą') end % Program funkcji xout = param * xin; yout = 2 * param * yin; end
Jak widać treść funkcji to dwie linijki związane z mnożeniem, a obsługa błędów to 17 linii programu. Czy da się to uprościć?
Funkcja arguments i walidacja błędów
Na szczęście od kilku edycji MATLABa do obsługi wyżej wymienionych błędów można używać funkcji arguments, która w prosty i czytelny sposób realizuje walidację argumentów wejściowych danej funkcji.
Walidacja błędów argumentów wejściowych funkcji powinna być wykonana przed głównym programem funkcji realizującym obliczenia, stąd fragment walidacyjny (i wywołanie arguments) musi być umieszczone w pierwszej linijce funkcji.
Funkcja arguments pozwala w sposób automatyczny nałożyć ograniczenia na rozmiar, typ i inne cechy argumentów (zmiennych przekazywanych do funkcji). W przypadku niezgodności tych cech z zadeklarowanymi, otrzymujemy szczegółowe informacje o występującym błędzie.
Sposób użycia funkcji arguments wyjaśnia poniższy rysunek:
Na dany argument przekazywany do funkcji można więc nałożyć następujące ograniczenia:
- Size – rozmiar argumentu (zmiennej),
- Class – klasa argumentu (zmiennej),
- Functions – inne ograniczenia zdefiniowane za pomocą dodatkowych funkcji, które umożliwiają na przykład sprawdzenie czy: argument przyjmuje wartości dodatnie, ujemne, skończone, rzeczywiste, całkowite, itd., czy argument ma wartości, większe, mniejsze, zawarte w pewnym przedziale, czy argument jest określonego typu i tym podobne.
Daje to duże możliwości kontroli nad przekazywanymi do funkcji zmiennymi, a w przypadku niezgodności cech zmiennej z deklaracją, na zgłoszenie błędu lub na automatyczną konwersję cech zmiennej do żądanego typu.
Popatrzmy na poniższy przykład. Mamy w nim funkcję podwojenie, która przyjmuje 5 argumentów. Rysuje ona rysunek dla wektorów x i y o kolorze kolor1, następnie podwaja wartości zmiennej y (i dodaje szum) oraz rysuje wektor o podwojonych wartościach w kolorze kolor2.
clear all; close all; clc; x_data = 0:pi/20:2*pi; y_data = sin(x_data); kolor1 = 'r' kolor2 = 'g' sigma = 0.1; podwojenie(x_data,y_data,sigma,kolor1,kolor2) function [] = podwojenie(x,y,sigma,kolor1,kolor2) % Obsługa błedów arguments x (1,:) double {mustBeInRange(x,0,10)} y (1,:) double sigma (1,1) double kolor1 (1,1) char kolor2 (1,1) char end % Program funkcji figure; plot(x,y,kolor1) y = 2 * y + sigma*randn(size(y)); hold on; plot(x,y,kolor2) end
Zwróćmy uwagę na fragment zawarty pomiędzy arguments .. end. Na zmienne przyjmowane przez funkcje mamy nałożone ograniczenia:
- Zmienna x musi być wektorem wierszowym o rozmiarze (1,:), typu double, oraz musi przyjmować wartości z zakresu 0 do 10,
- Zmienna y, ma podobne ograniczenia, lecz nie ma ograniczenia na zakres wartości,
- Zmienne kolor1 i kolor2 muszą być typu char.
Widzicie jakie to proste? Bez używania warunków if.. elseif.. end i dodatkowych funkcji w łatwy sposób zostały zdefiniowane ograniczenia na zmienne. Co więcej w przypadku niezgodności cech zmiennej z deklaracją, automatycznie otrzymamy komunikat o błędzie.
Zachęcam do stosowania w funkcjach struktury arguments … end. Obsługa błędów może znacznie ułatwić życie na etapie używania funkcji. Szczegółowy opis znajdziecie w dokumentacji MATLABa.