Na wykładzie mówiłem o różnych sposobach tworzenia aplikacji. Tu zajmiemy się narzędziem Application Compiler.
Ponieważ nie będę w stanie sprawdzić przygotowanej przez Państwa Aplikacji — będę prosił o przesłanie jej kodu źródłowego zawierającego w komentarzach opis funkcjonowania.
Zadania…
…Państwa sprowadzą się zatem do:
- Wymyślenia jakiejś ciekawej funkcji rozwiązującej zadany problem w MATLABie.
- Zaprogramowania jej (i przetestowania)
- Konwersji do aplikacji z a pomocą narzędzia Application Compiler
- Przetestowania jej.
Ja oczekuję kodu źródłowego (punkt 2 powyżej, polik .m
albo .mlx
) i krótkiego sprawozdania podsumowującego wyniki. Sprawozdanie może być zawarte na końcu pliku żródłowego jako komentarze albo jako osobny plik…
Przykłady możliwych aplikacji
- Program który prosi o wskazanie pliku
.csv
odpytuje o nazwy (dwu lub więcej) kolumn, które mają być zaprezentowane na wykresie i produkuje wykres. - Program, który będzie rodzajem kalkulatora pozwalającego na wykonanie jakichś obliczeń.
- …
Gdyby zadanie wydawało się za łatwe można spróbować stworzyć aplikację według przepisu podanego na wykładzie korzystając z aplikacji Design App.
Testowanie stworzonej aplikacji
W przypadku aplikacji utworzonej z użyciem Application Compiler powstaje kartoteka zgodna z nazwą aplikacji; przechodzimy (MATLABem) do pod-kartoteki o nazwie for_testing
i tam wypisujemy
w przypadku Windows
!app_name parametry
w przypadku linuksa
!./app_name parametry
Więcej informacji na stronie Create Standalone Application from MATLAB Function
Warto wiedzieć
Błędy
Ponieważ zakładamy, że program może (w przypadku, na przykład, kalkulatora) komunikować się z użytkownikiem — w sposób naturalny mogą pojawić się błędy…
Do obsługi takich sytuacji służą polecenia try
oraz catch
.
Używa się ich w sposób następujący:
try
statements
catch exception
statements
end
Wyobraźmy sobie taką sytuację:
A = rand(3);
B = ones(5);
C = [A; B];
Ponieważ rozmiary tablic A
i B
nie zgadzają się nie da się wykonać operacji wskazanej w trzeciej linii i pojawi się komunikat błędu:
Error using vertcat
Dimensions of arrays being concatenated are not consistent.
Można chronić się przed taką sytuacją w sposób następujący:
try
C = [A; B];
catch ME
if (strcmp(ME.identifier,'MATLAB:catenate:dimensionMismatch'))
msg = ['Dimension mismatch occurred: First argument has ', ...
num2str(size(A,2)),' columns while second has ', ...
num2str(size(B,2)),' columns.'];
causeException = MException('MATLAB:myCode:dimensions',msg);
ME = addCause(ME,causeException);
end
rethrow(ME)
end
Parę słów na temat ME
- W przypadku pojawienia się błędu wykonania konstruowany jest obiekt zawierający informacje o błędzie oraz wszystkie niezbędne informacje)
catch
wychwytuje ten obiekt i sprawdza na czym polega ten błąd, porównując identyfikator błędu (MATLAB:catenate:dimensionMismatch
)- W przypadku gdy porównanie jest pozytywne konstruuje odpowiedni komunikat informujący o tym (pobiera rozmiary tablic) i następnie
- używa funkcji
MEexception()
tworząc „nowy” błąd o identyfikatorzeMATLAB:myCode:dimensions
i odpowiednio skonstruowanym komunikacie błędu. - Funkcja
rethrow()
„podmienia” błąd na nasz…
Trochę to skomplikowane, ale można potrenować, na przykład tak:
try
C = [A; B];
catch ME
display(ME.identifier)
display(ME.message)
end
teraz wszystko co robimy po wystąpieniu błędu drukujemy najważniejsze składowe tej struktury. Inne składowe to:
ME.cause
ME.stack
(zawiera informację o funkcji/pliku skryptu w której wystąpił błąd (i historii wywołań) oraz numerze linii)
Inne działanie w przypadku wykrycia błędu może wyglądać tak:
try
a = notaFunction(5,6);
catch
warning('Problem using function. Assigning a value of 0.');
a = 0;
end
Teraz kod chce wywołać funkcję, która w kodzie nie jest dostępna (notaFunction(5,6)
), powoduje to błąd, który jest rozwiązywany w ten sposób, że wywołujemy funkcję warning()
zawierającą komunikat, a zmiennej a
nadajemy wartość zero.
W przypadku gdy może być kilka przyczyn błędu (w powyższym przykładzie istnieje plik notaFunction.m
, który nie jest funkcja) można sprawę rozwiązać tak:
try
a = notaFunction(5,6);
catch ME
switch ME.identifier
case 'MATLAB:UndefinedFunction'
warning('Function is undefined. Assigning a value of NaN.');
a = NaN;
case 'MATLAB:scriptNotAFunction'
warning(['Attempting to execute script as function. '...
'Running script and assigning output a value of 0.']);
notaFunction;
a = 0;
otherwise
rethrow(ME)
end
end
I wykonanie kończy się albo nadaniem wartości NaN
zmiennej a
, albo wykonaniem kodu w pliku notaFunction.m
i nadani zmiennej a
wartości zero.
Funkcja eval()
Funkcja eval()
wylicza wartość podanego jako tekst wyrażenia.
>> eval("2*pi")
ans =
6.2832
>> z=eval("plus(2,3);")
z =
5
>> eval("y = 2 * 3")
y =
6
Przekazywanie argumentów do aplikacji
Bardzo prosta funkcja:
function y = apka(a, b, c)
y = a + b / c;
display(y);
end
Użycie jej w MATLABie jest proste
>> apka(1, 2, 3);
y =
1.6667
Gdy uruchamiamy ją po zbudowaniu aplikacji (trzeba wejść do stworzonej kartoteki (apka
) i podkartoteki for_testing
)
!./apka 1 2 3
da w wyniku
y =
49.9804
Czemu?
Sprawa jest dosyć skomplikowana, ale otwierając okno terminala (czy to w Windows czy to w linuksie, czy też na Macu) i wydając polecenie, na przykład dir
tworzona jest specjalna struktura danych zawierająca tekst polecenia. W przypadku języka C/C++ będzie to tablica typu char**
(każdy element tablicy jest wskaźnikiem na wyraz tekstu polecenie); dodatkowo przekazywana jest liczba wyrazów…
(Dygresja:
Jeżeli ktoś programował w C++ to być może pamięta taką minimalną zawartość funkcji main()
#include <iostream>
int main(int argc, char **argv)
{
return 0;
}
argc
zawiera liczbę wyrazów
argv
to postać linii wywołującej program
To jest właśnie to.
)
I teraz zmieniłem postać funkcji apka
na poniższy:
function y = apka(a, b, c)
display(a);
display(b);
display(c);
y = a + b / c;
display(y);
end
Zbudowałem i uruchomiłem
./apka 1 2 3: Signal 121
>> !./apka 1 2 3
a =
'1'
b =
'2'
c =
'3'
y =
49.9804
Zmienne a
, b
i c
wewnątrz funkcji to wartości znakowe, a operacja wykonywana jest na wartościach kodów ASCII odpowiadających poszczególnym znakom…
Gdy chcemy korzystać z „automatycznego” przekazywania wartości z linii polecenia, trzeba wewnątrz funkcji dokonać konwersji, na przykład za pomocą funkcji str2num()
albo str2double()
i jest OK
function y = apka(a, b, c)
if ischar(a)
a = str2double(a);
end
if ischar(b)
a = str2double(b);
end
if ischar(c)
a = str2double(c);
end
y = a + b / c;
display(y);
end
i teraz już
>> !./apka 1 2 3
y =
1.6667