W poprzedniej części kursu wyjaśniłem, jak łatwo w Octave można utworzyć własne programy (tzw. skrypty). Funkcje użytkownika najłatwiej można zdefiniować właśnie w formie skryptów.
Załóżmy, że naszym celem jest zdefiniowanie
funkcji o nazwie sin5, która dla argumentu x obliczać będzie wartość
wyrażenia x - x3/6 + x5/120. Aby zdefiniować taką funkcję:
sin5.mfunction y = sin5(x) y = x - x**3/6 + x**5/120; endfunction
Uwaga! Powyższa definicja nie jest optymalna! Niedługo ją poprawimy!
Funkcje tę wywołujemy w Octave w naturalny sposób:
> sin5 (0.1) ans = 0.099833
Ogólnie, podczas definiowania funkcji w pliku obowiązują następujące zasady:
m
(tj. mieć postać nazwa_funkcji_m).function i endfunction.function y = f(a, b, c)
function [y, eps, xi] = f(a, b, c)
y = x - x**3/6; y = y + x**5/120;Niezakończenie jakiejś instrukcji średnikiem spowoduje wyświetlenie wykonywanych w niej obliczeń – ta cecha przydaje się np. podczas szukania błędów w skrypcie, ale rzadko kiedy ma zastosowanie w gotowej, przetestowanej funkcji, stąd zalecenie, by instrukcje kończyć średnikiem.
cos, czyli
funkcja wyznaczająca cosinus kąta:
> cos([0, 0.1, 0.2]) ans = 1.00000 0.99500 0.98007
Jak widać, jeżeli argumentem funkcji cos jest wektor, wynikiem jest wektor utworzony
z wartości tej funkcji dla wszystkich elementów wektora [0, 0.1, 0.2],
czyli
cos([0, 0.1, 0.2]) = [cos(0), cos(0.1), cos(0.2)]
Ogólnie, jeżeli f jest odwzorowaniem, a v macierzą n na m, to
f(v) też jest macierzą n na m,
przy czym jeżeli podstawimy w = f(v), to spełniona będzie zależność
w(k,l) = f(v(k,l)). Innymi słowy, w k-tym wierszu i l-tej kolumnie
macierzy f(v) znajduje się wartość funkcji f dla argumentu v(k,l).
Informacja o tym, czy dana funkcja Octave jest odwzorowaniem, znajduje się w dokumentacji tej funkcji
(help), w której odwzorowania określane są jako mapper function lub mapping function,
zwykle już w pierwszym wierszu opisu.
Odwzorowania są w Octave bardzo ważną klasą funkcji, gdyż umożliwiają rozwiązywanie wielu podstawowych problemów numerycznych, takich jak znajdowanie pierwiastków równań z wieloma niewiadomymi, rozwiązywanie równań różniczkowych czy obliczanie pól powierzchni (całek), a także tworzenie wykresów funkcji.
Jak można się łatwo przekonać, nasza funkcja sin5 nie jest odwzorowaniem
i nie można jej wywoływać z argumentami innymi niż skalary, co znacznie ogranicza jej użyteczność.
Można temu jednak łatwo zaradzić. Aby funkcja sin5 stała się odwzorowaniem,
wystarczy poprzedzić kropką wszystkie występujące w jej definicji operatory potęgowania:
function y = sin5(x) y = x - (x.**3)/6 + (x.**5)/120; endfunction
Ogólnie, kropką można poprzedzić dowolny operator "iloczynowy",
czyli operator mnożenia (.*, dzielenia (./ lub .\) i
potęgowania (.** lub .^). To, kiedy stosować tę kropkę,
zależy od następujących czynników:
x/2 jest zawsze równoważne wyrażeniu x./2. x * y macierz x musi mieć tyle kolumn,
ile wierszy ma macierz y.x/y jest równoważne x*(y-1),
a więc wiąże się z kosztownym obliczeniem odwrotności y.x\y ma wartość (x-1)*y,
która jest wyznaczana szybkim algorytmem niewymagającym wyznaczania x-1.x.*y i x./y macierze x i y
muszą mieć ten sam rozmiar. Operacje z kropką wykonywane są "element po elemencie". Np.
> m = [1 -1; -1 1] m = 1 -1 -1 1 > v = [1 2; 3 4] v = 1 2 3 4 > m .* v ans = 1 -2 -3 4 > m * v ans = -2 -2 2 2
.\", ".+" i ".–".> 2./mzostanie zinterpretowane następująco:
2 ./ m
Na zakończenie warto dodać, że jeżeli w pliku zawierającym definicję funkcji
umieścimy komentarz (najlepiej przed jej definicją),
będzie on wyświetlany przez Octave po wydaniu polecenia help nazwa_funkcji
(np. help sin5). Stąd też ostateczna
zawartość pliku sin5.m może wyglądać następująco:
# Funkcja sin5 przybliża wartość funkcji sin(x) za pomocą wielomianu stopnia 5. # # Przybliżenie to jest całkiem dobre dla małych wartości x, # np. dla |x| < 1. # function y = sin5(x) y = x - x.**3/6 + x.**5/120; endfunction
function i endfunction?x*y i x.*y?x/y i x./y?x**2 i x.**2?x+y i x.+2?f(x) zadaną wzorem:f(x) = x * sin2(x) * sqrt (abs (1 - x)) + x*cos(x)
gdziesqrt oznacza pierwiastek kwadratowy, a abs to wartość bezwzględna.
f(x) dla 0 ≤ x ≤ 2.5.
W tym celu możesz użyć następującego ciągu poleceń:
> x = 0 : 0.001 : 2.5; > y = f(x); > plot (x,y)Pierwsze z nich definiuje wektor "x-ów", drugie – wektor wartości w odpowiednich "x-ach", a trzecie powoduje wygenerowanie takiego wykresu jak ten:

f(x) = 0 ma pierwiastek z równy
w przybliżeniu 2.2. Wyznacz jego wartość za pomocą polecenia fsolve:
> z = fsolve("f", 2.2)
f(x) dla
0 ≤ x ≤ z a
osią rzędnych (tj. y = 0) (na poniższym rysunku zostało ono zaznaczone kolorem zielonym):

Możesz posłużyć się następującym poleceniem:
> quad("f", 0, z)
quad i fsolve.
Jakie jest znaczenie ich parametrów? Jak można ocenić dokładność rozwiązań?