Instrukcja warunkowa

Transkrypt

Instrukcja warunkowa
Rozdział 3
Instrukcja warunkowa
{chap:warunek}
IC
Chcemy napisać własna˛ funkcj˛e, obliczajac
˛ a˛ wartość bezwzgl˛edna.˛
Taka funkcja istnieje, nazywa si˛e abs() ale chwilowo załóżmy, że nie wiemy
o jej istnieniu a jest nam potrzebna. Jedyne wyjście to napisać ja˛ samodzielnie.
Matematyczna funkcja |x| jest definiowana jako:
|x| =
(
x dla x ­ 0
−x dla x < 0
SZK
O ile umiemy już pisać całkiem skomplikowane wyrażenia, to tutaj napotykamy problem, chociaż funkcja nie wyglada
˛ jakoś skomplikowanie.
Źródłem problemu jest fakt, że umiemy pisać funkcje, które realizuja˛ pewien,
czasami nawet złożony, przepis. Natomiast funkcja |x| jest dana w postaci dwu
przepisów, jeden przepis |x| = x obowiazuje
˛
dla x ­ 0 a drugi |x| = −x obowiazuje
˛
dla x < 0.
3.1 Instrukcje sterujace
˛
Przepływ – rysunek!
3.2 Instrukcja warunkowa
Potrzebna jest nam instrukcja warunkowa, czyli możliwość przełaczania
˛
działania
programu w zależności od danych.
Pierwsza postać takiej instrukcji zapisuje si˛e jako:
if
warunek
...
...
end%if
25
{sec:if}
{if:cond}
Rozdz. 3
26
Po słowie kluczowym if (ang. if – jeżeli) nast˛epuje warunek. Jeśli warunek
ten jest spełniony to wykonywane sa˛ instrukcje mi˛edzy linia˛ zawierajac
˛ a˛ if a linia˛ zawierajac
˛ a˛ end%if. Instrukcji wykonywanych mi˛edzy tymi liniami może być
dowolna ilość (z zerowa˛ włacznie).
˛
Istnieje też poszerzona forma instrukcji warunkowej:
if
warunek
...
else
...
end%if
IC
Poczatek
˛ jest identyczny, po słowie if jest warunek, nast˛epnie dowolna ilość
linii wykonywanych kiedy warunek jest spełniony. Potem jest nowy element, słowo
kluczowe else (ang. else – w przeciwnym przypadku) po którym nie ma warunku, za to w kolejnych liniach sa˛ instrukcje wykonywane w przypadku gdy warunek
po if nie jest spełniony.
Najprostszymi warunkami sa˛ relacje mniejszości < i wi˛ekszości >. Relacje
słabej mniejszości lub wi˛ekszości wpisujemy w podobny sposób ale ponieważ na
klawiaturze nie ma klawiszy odpowiadajacych
˛
matematycznym ­ i ¬ wi˛ec te relacje zapisuje si˛e za pomoca˛ pary znaków: <= lub >=.
SZK
Tak wi˛ec warunek sprawdzajacy
˛ czy x jest mniejszy od zera miałby postać:
if x < 0
To pozwala nam napisać funkcj˛e obliczajac
˛ a˛ wartość bezwzgl˛edna.˛ Nazwijmy
ja˛ wbzw aby móc używać wbudowanej abs. Na końcu umieścimy wywołanie naszej
funkcji dla trzech charakterystycznych wartości aby przekonać si˛e, czy funkcja
działa poprawnie1
{prog:3:1}
1 function y=wbzw(x)
2
if x < 0
3
y=-x;
4
else
5
y=x;
6
end%if
7 end%function
8 wbzw(-7)
9 wbzw(0)
10 wbzw(0.1)
1
Sprawdzanie poprawności programu to skomplikowana i żmudna praca. Tutaj jedynie sprawdzamy czy w trzech ważnych przypadkach program da poprawne wyniki.
Wersja: 4 października 2009
Rozdz. 3
27
3.3 Zagł˛ebione warunki
{sec:zagl}
f (x) =


 0
x<1
1 1¬x¬2


2 x>2
Funkcja ta wnosi t˛e komplikacj˛e, że jest dana trzema przedziałami a instrukcja
warunkowa pozwala na wyróżnienie dwu różnych przepisów.
Istnieje kilka metod rozwiazania
˛
tego zadania, pokażemy metod˛e polegajac
˛ a˛ na
rozłożeniu funkcji na dwa podprzedziały i sprowadzenie jej do co najwyżej dwu
różnych przepisów w każdym.
Funkcja jest zdefiniowana w trzech przedziałach: (−∞, 1), [1, 2] i (2, ∞). Możemy ja˛ jednak (w nieco sztuczny sposób) rozdzielić na dwie funkcje f1 (x) i f2 (x):
f1 (x) x < 1
f2 (x) x ­ 1
IC
(
f (x) =
gdzie f1 (x) jest określona w przedziale (−∞, 1) i w tym przedziale jest opisana
wzorem:
f1 (x) = 0
natomiast funkcja f2 (x) jest określona w przedziale [1, ∞) i jest dana dwoma przepisami na podprzedziałach:
1 x¬2
2 x>2
SZK
f2 (x) =
(
Równie dobry byłby podział na przedziały (−∞, 2] w którym byłyby dwa przepisy i (2, ∞) w którym byłby jeden przepis. Tutaj jednak arbitralnie przyjmujemy
pierwszy sposób.
W takim razie szkic programu wygladałby
˛
nast˛epujaco:
˛
{prog:3:2}
1 if x < 1
2
% przepis na f1(x)
3 else % czyli x>= 1
4
% tu przepis na f2(x)
5 end%if
Puste miejsce przeznaczone na przepis funkcji f1 (x) łatwo wypełnić, gdyż ta
funkcja zawsze jest równa 0 a wi˛ec trzeba wpisać do wartości zwracanej przez
funkcj˛e zero.
Natomiast przepis na funkcj˛e f2 (x) jest troch˛e bardziej skomplikowany, gdyż
składa si˛e z dwu cz˛eści w zależności od wartości x. W tym miejscu trzeba wi˛ec
skorzystać z instrukcji warunkowej. Jedyna nowość, że ta instrukcja warunkowa
b˛edzie zawarta wewnatrz
˛ innej.
{prog:3:3}
1 function y=azor(x)
Wersja: 4 października 2009
Rozdz. 3
28
2
if x < 1
3
y=0; % przepis na f1(x)
4
else % czyli x>= 1
5
% tu przepis na f2(x)
6
if x <= 2
7
y=1; % pierwsza cz˛
eść przepisu
8
else
9
y=2; % druga cz˛
eść przepisu
10
end%if
11
end%if
12 end%function
IC
Warto przeanalizować t˛e funkcj˛e, gdyż wydaje si˛e, że brak jest tam kompletnych warunków logicznych.
Porównujac
˛ zapis matematyczny i nasz program widzimy, że w zapisie matematycznym zawsze sa˛ kompletne warunki natomiast w programie nie. Wynika to
z faktu, że przepis matematyczny, nawet we fragmencie jest jednoznaczny, czyli
cytujac
˛ tylko jedna˛ lini˛e
f (x) =


 ...
1 1¬x¬2

 ...
SZK
nie wiemy jaki jest przepis na f (x) poza przedziałem [1, 2] ale w tym przedziale
funkcja jest całkowicie i jednoznacznie określona.
Programu nie można czytać wyrywkowo, to co si˛e dzieje w jednej linii może
zależeć od tego co było w liniach poprzednich. Program zawsze wykonuje si˛e po
kolei. Z tego wynika dosyć odmienna od matematycznego postać zapisu.
Jeśli spełniony b˛edzie pierwszy warunek if x < 1, to wykonana zostanie instrukcja przypisania y=0 natomiast zignorowane zostanie wszystko mi˛edzy pierw˛
je (ostatnim) end%if. Tak wi˛ec na końcu funkcji, czyli
szym else a zamykajacym
w momencie napotkania end%function w zmiennej y b˛edzie wartość 0.
Do linii po pierwszym else możemy dotrzeć tylko jeśli warunek if x < 1
nie jest spełniony, czyli w tych liniach niejawnie obowiazuje
˛
warunek x ­ 1. Jeśli
jest tam zagł˛ebiony warunek if x <= 2, to spełnienie tego warunku oznacza, że
zarówno x ­ 1 jak i x ¬ 2 czyli, że x ∈ [1, 2]. Wtedy (jedynie wtedy) wykona si˛e
instrukcja przypisania y=1;.
˛
(z pierwszego
Podobnie, po zagł˛ebionej (drugiej) instrukcji else obowiazuje
else) x ­ 1 oraz x > 2 gdyż nie jest spełniony if x <= 2. W takim razie y=2;
wykona si˛e tylko, jeśli x > 2.
Z faktu, że program wykonuje si˛e linia po linii wynika możliwość znacznego
uproszczenia programu2
{prog:3:4}
1 function y=azor(x)
2
y=0;
2
Zalecane tylko wtedy, gdy ktoś dokładnie rozumie o co chodzi.
Wersja: 4 października 2009
Rozdz. 3
29
3
if x >= 1
4
y=1;
5
end%if
6
if x > 2
7
y=2;
8
end%if
9 end%function
Punkt izolowany
Rozważmy funkcj˛e opisana˛ wzorem:
H(x) =


 0 x<0
1
x=0
2

 1 x>0
SZK
1 function y=H(x)
2
if x < 0
3
y=0;
4
else
5
if x=0
6
y=1/2;
7
else
8
y=1;
9
end%if
10
end%if
11 end%function
IC
Należałoby od razu zaznaczyć, że program obliczajacy
˛ wartość funkcji, zawierajac
˛ a˛ izolowana˛ wartość w punkcie może zachowywać si˛e w sposób nie do
końca zgodny z przewidywaniami. Jeśli operujemy na liczbach rzeczywistych to
x
dla x=(4/3-1)*3-1 funkcja nie wyliczy wartości 21 , gdyż z powodu zaokragleń
˛
wcale nie b˛edzie zero. Do tego problemu wrócimy przy okazji funkcji kwadratowej
natomiast teraz napiszmy t˛e funkcj˛e tak jak jest zadana, czyli z pełna˛ świadomo- {punkt-izolowany}
ścia,˛ że jeśli obliczamy wartość funkcji dla argumentu teoretycznie równego zero
ale obarczonego bł˛edem zaokraglenia
˛
to nie uzyskamy 12 .
Program, który obliczałby wartości takiej funkcji zapewne zostałby napisany
w ten sposób:
{prog:3:5}
12 H(-7)
13 H(0)
14 H(0.1)
Wykonanie tego programu pokazuje, że nie działa on poprawnie. Wartość
funkcji H(−7) jest rzeczywiście zero, wartość funkcji H(0, 1) jest jeden ale wartość H(0) wynosi też jeden.
Nie jest to problem zaokragleń,
˛
gdyż zero podane jako argument nie jest przybliżeniem ale daleko poważniejsza komplikacja.
Wersja: 4 października 2009
Rozdz. 3
30
Aby zrozumieć dlaczego to nie działa poprawnie trzeba długiej analizy relacji
równości.
3.4 Relacja równości
{sec:war}
{prog:3:6}
1
2
3
4
5
IC
Warunek, który pojawia si˛e po instrukcji if ma charakter warunku logicznego,
jest albo prawdziwy (spełniony) albo fałszywy (niespełniony). W logice jednym ze
sposobów oznaczania prawdy i fałszu jest oznaczanie liczbowe, kiedy prawda ma
wartość 1 a fałsz ma wartość 0.
W takim świetle można si˛e domyślać, że zapis typu:
if 1
oznacza warunek zawsze spełniony (prawdziwy) a zapis:
if 0
oznacza warunek fałszywy.
Rzeczywiście prosty programik:
if 1
disp("Warunek prawdziwy");
else
disp("Warunek fałszywy");
end%if
SZK
Pozwala przekonać si˛e, że istotnie tak to działa. Zmiana z 1 na 0 w pierwszej
linii pozwala też stwierdzić, że zmienia si˛e komunikat.
Nie ma sensu formułować takich warunków, bo warunek zawsze spełniony
oznacza, że linie wewnatrz
˛ bloku si˛e zawsze wykonaja,˛ wi˛ec można nie pisać if.
W przypadku warunku fałszywego, linie wewnatrz
˛ bloku w ogóle si˛e nie wykonaja,˛ wi˛ec po co w ogóle to pisać?3
Zastanówmy si˛e natomiast, co mógłby oznaczać warunek w formie:
if x
Pami˛etajac,
˛ że nazwa zmiennej x jest tylko odwołaniem do wartości przecho˛
że jeśli w zmiennej x jest 1 to dostaniemy waruwywanej w x należałoby sadzić,
nek spełniony. Jeśli natomiast w zmiennej x jest 0 to warunek nie b˛edzie spełniony.
Możemy przekonać si˛e o tym piszac
˛ prosty programik:
{prog:3:7}
1
2
3
4
5
6
x=1;
if x
disp("Warunek prawdziwy");
else
disp("Warunek fałszywy");
end%if
3
Czasami wygodnie jest tego użyć aby chwilowo wykomentować dłuższy fragment programu.
Wersja: 4 października 2009
Rozdz. 3
31
IC
Zmieniajac
˛ przypisanie w pierwszej linii wartości x na 0 możemy rzeczywiście
przekonać si˛e, że sprawdzana jest wartość wpisana do zmiennej x.
Naturalne pytanie, które si˛e tutaj narzuca, to co byłoby gdyby x nie było równe
ani 0 ani 1 tylko np. -7 albo 3.14. Czy jest to prawda czy fałsz?
Dosyć zawiła odpowiedź na to proste pytanie jest taka, że OCTAVE (i praktycznie wszystkie inne j˛ezyki programowania) nie używa typu logicznego (boolowskiego), który dopuszczałby tylko wartości 1 i 0. O ile wprowadzenie takiego typu
nie jest trudne, to znacznie rozbudowuje i komplikuje j˛ezyk, co nie jest korzystne.
Dlatego zamiast typu boolowskiego, powszechnie używa si˛e liczb całkowitych (albo w ogóle liczb). Zawieraja˛ one wartości 0 i 1, wi˛ec moga˛ służyć do oznaczania
prawdy i fałszu. Jak traktować wszelkie pozostałe wartości, to jest kwestia umowy. Można sobie wyobrazić różne sensowne umowy, np. liczby dodatnie to prawda
a pozostałe to fałsz. Jednak powszechnie przyj˛eto inna˛ umow˛e (równie dobra˛ jak
każda): zero to fałsz, reszta to prawda. Może wydać si˛e to nieco dziwaczne, że -7
to prawda ale jest to kwestia przyzwyczajenia. Jeśli rozumiemy sens warunku, to
okazuje si˛e, że można to wykorzystać w bardzo wygodny sposób.
W konsekwencji warunek:
if x
w praktyce odpowiada warunkowi, czy x jest różne od zera. Poczatkuj
˛ acym
˛
programistom należy odradzić stosowanie tego typu konstrukcji, przyniosa˛ wi˛ecej szkody
niż pożytku, raczej należałoby zastosować jawny operator relacji „różne” o którym
w dalszym ciagu.
˛
To wszystko służyć ma analizie co oznacza zapis:
if x=y
SZK
Wbrew pozorom nie jest to warunek czy x jest równe y.
Ta konstrukcja składa si˛e w rzeczywistości z dwu cz˛eści, cz˛eść pierwsza to if
x a cz˛eść druga to x=y. Najpierw jest wykonywana ta druga. Jak to podkreślono na
str. 20 instrukcja:
x=y
jest instrukcja˛ przypisania, czyli nowa wartość x staje si˛e taka jak wartość y.
Potem wykonywana jest cz˛eść pierwsza: if x . Sumaryczny efekt całej instrukcji jest nast˛epujacy:
˛ wartość wpisana do zmiennej x jest nadpisywana przez
wartość ze zmiennej y a nast˛epnie sprawdzane jest, czy w zmiennej x jest liczba
różna od zera.
Przykład:
{prog:3:8}
1
2
3
4
5
6
7
x=0;
y=0;
if x=y
disp("Spełniony");
else
disp("Niespełniony");
end%if
Wersja: 4 października 2009
Rozdz. 3
32
wypisze, że warunek jest niespełniony, chociaż x jest równy y. Identyczny efekt
˛
inna˛ wartość na poczatku,
˛
gdyż w linii z warunkiem
b˛edzie jeśli x ma jakakolwiek
if wartość x jest nadpisywana przez 0.
Niemal identyczny program:
{prog:3:9}
x=0;
y=1;
if x=y
disp("Spełniony");
else
disp("Niespełniony");
end%if
zawsze b˛edzie pokazywał, że warunek jest spełniony, znowu zupełnie niezależnie od poczatkowej
˛
wartości x. Tym razem zanim warunek zostanie wyliczony
x jest nadpisywane przez 1 a to jest prawda.
W tym momencie można wrócić do naszego programu obliczajacego
˛
wartości
funkcji ze str. 29 teraz jest zrozumiałe, że linia:
if x=0
wcale nie sprawdza, czy x jest równa zero. Ta linia wpisuje do zmiennej x wartość
zero a przy okazji warunek staje si˛e fałszywy.
Operator porównania jest zapisywany w postaci podwójnego znaku ==. Tak
wi˛ec to, co chcieliśmy napisać powinno wygladać:
˛
if x==0
Ten drugi znak równości jest tutaj krytyczny, bez niego program działa, tylko
w zupełnie inny sposób. Do tego znak = jest zgodny z matematycznym operatorem
równości, wi˛ec bardzo trudno dostrzec bład
˛ 4 . Nie jest to tylko problem poczatku˛
jacych.
˛
Takie bł˛edy popełniaja˛ również najbardziej doświadczeni programiście. Po
˛ Jednym ze sposobów, jak można ustrzec si˛e przed
prostu zapis x=y jest łudzacy.
tym problemem, jest wykorzystanie faktu, że relacja równości jest przemienna natomiast operator przypisania nie. Jeśli zamiast if x==0 napiszemy:
if 0==x
to jest to równoważne, choć nieco dziwnie wygladaj
˛ ace.
˛ Ale jeśli, przez pomyłk˛e,
„zgubimy” jeden znak równości to linia:
if 0=x
jest nielegalna i OCTAVE odmówi jej wykonania.
Ten prosty sposób działa tylko wtedy, kiedy sprawdzamy czy zmienna jest równa stałej. W przypadku if x==y zamiana stronami nic nie pomoże ale można wtedy napisać:
if x+0==y
Znowu, przy braku drugiego znaku równa si˛e, nielegalne jest użycie wyrażenia po
lewej stronie instrukcji przypisania a wyrażenie w relacji porównania jest popraw-
SZK
IC
1
2
3
4
5
6
7
4
Paradoksalnie, programiści Fortranu nie znaja˛ takiego problemu, gdyż w Fortranie operator porównania miał postać .EQ.
Wersja: 4 października 2009
Rozdz. 3
33
ne.
Pozostaje jeszcze do wyjaśnienia kwestia, dlaczego OCTAVE pozwala na użycie
przypisania tam, gdzie z natury chcemy użyć porównania? Wbrew pozorom, może
to być bardzo dogodne:
{prog:3:10}
1
2
3
4
5
6
...
if x=2*y+1
z=p/x;
else
disp("Nie wolno dzielić przez zero");
end%if
IC
W przypadku użycia wartości zmiennej jako warunku logicznego, OCTAVE wypisuje jedynie ostrzeżenie: warning: suggest parenthesis around assignment used as truth value co należałoby przetłumaczyć: Ostrzeżenie: sugeruj˛e użycie nawiasów wokół instrukcji przypisania użytej jako warunek logiczny.
W wi˛ekszości przypadków użycie w tym miejscu przypisania jest pomyłka,˛
wi˛ec OCTAVE ostrzega przed tym. Natomiast, jeśli rzeczywiście chcielibyśmy użyć
konstrukcji if x=y to należałoby to zapisać w postaci if (x=y) , czyli informujemy OCTAVE „Wiem co robi˛e!”.
Konsekwencja˛ tego jest, że nie tylko nie ma potrzeby ujmować warunków w
nawias5 ale wr˛ecz należy tego unikać, za wyjatkiem
˛
świadomego użycia instrukcji
przypisania.
SZK
3.5 Zapis strukturalny.
Warto zwrócić uwag˛e na specyficzny zapis stosowany w przypadku instrukcji warunkowej, nie piszemy
if warunek
instrukcja1
instrukcja2
else
instrukcja3
end%if
ale piszemy
if warunek
instrukcja1
instrukcja2
else
instrukcja3
end%if
5
W j˛ezyku C użycie nawiasu w warunku logicznym jest obowiazkowe.
˛
Wersja: 4 października 2009
Rozdz. 3
34
instrukcja1
instrukcja2
if warunek1
instrukcja3
instrukcja4
if warunek2
instrukcja5
else
instrukcja6
end%if
else
instrukcja7
end%if
instrukcja8
IC
Jest to tzw. zapis strukturalny. Polega on na tzw. wci˛eciach, czyli wpisywaniu instrukcji wykonywanych warunkowo nie od poczatku
˛ linii ale przesuni˛etych o kilka
pozycji w prawo.
Taki sposób formatowania jest całkowicie ignorowany przez OCTAVE ale nie
służy on programowi a programiści. Ma za zadanie ułatwić analiz˛e, które instrukcje
i przy jakich warunkach zostana˛ wykonane.
SZK
Widać, że instrukcje 1 i 2 wykonuja˛ si˛e zawsze. Potem jeśli spełniony jest warunek1 to wykonuja˛ si˛e instrukcje 3 i 4. Nast˛epnie instrukcja 5 albo 6, w zależności
od warunku 2.
3.6 Funkcja kwadratowa
Funkcja kwadratowa. Dla zadanych współczynników a, b, c wielomianu y(x) =
ax2 + bx + c znaleźć (rzeczywiste) miejsca zerowe czyli rozwiazanie
˛
równania
2
ax + bx + c = 0. Jak wiadomo należy obliczyć wyróżnik równania ∆ = b2 −√4ac
a nast˛epnie jeśli ∆ jest wi˛eksza od zera to mamy dwa pierwiastki: x1,2 = b±2a ∆ ,
b
jeśli ∆ jest równa zero to mamy jeden podwójny pierwiastek x = 2a
a kiedy ∆ jest
ujemna to brak pierwiastków rzeczywistych.
Jak to już zaznaczyliśmy na str. 29, formułowanie warunków w punkcie dla
argumentów rzeczywistych może dać dość nieoczekiwane efekty gdyż komputer
operuje na skończonej reprezentacji binarnej liczb a nie na wartościach dokładnych.
Jeśli ∆ jest dodatnia ale mała, to oznacza to, że istnieja˛ dwa różne pierwiastki
ale bardzo blisko położone na osi liczbowej. Dla dostatecznie małej ∆ b˛eda˛ si˛e
różnić wartościami na odpowiednio dalekich cyfrach znaczacych.
˛
Jeśli różnia˛ si˛e
na dwudziestym miejscu znaczacym,
˛
a reprezentacja liczb w komputerze zawiera
Wersja: 4 października 2009
Rozdz. 3
35
jedynie 16-17 cyfr znaczacych
˛
to pomimo, że sa˛ to dwa różne pierwiastki i tak nie
jesteśmy w stanie odróżnić tego od przypadku, kiedy jest jeden wspólny pierwiastek czyli ∆ = 0.
Dlatego nie ma sensu rozróżniać przypadków ∆ = 0 i ∆ > 0
Funkcja kwadratowa:
% inicjujemy współczynniki równania kwadratowego
a=1; b=2; c=1;
Delta=b*b-4*a*c;
if Delta >= 0
x1=(-b-sqrt(Delta))/(2*a)
x2=(-b+sqrt(Delta))/(2*a)
else
disp("Brak pierwiastków rzeczywistych");
end%if
IC
1
2
3
4
5
6
7
8
9
{prog:3:11}
Uzupełnijmy nasz program o sprawdzenie czy współczynnik a równania kwadratowego nie jest przypadkiem równy zero, gdyż wtedy nie byłaby to parabola
tylko prosta (lub punkt – jeśli także b = 0).
Do tego potrzebujemy operator „różny od”. Taki operator ma postać pary znaków ˜=6 .
Nasz program wygladałby
˛
nast˛epujaco:
˛
{prog:3:12}
SZK
1 % inicjujemy współczynniki równania kwadratowego
2 a=1; b=2; c=1;
3 if a ˜= 0
4
Delta=b*b-4*a*c;
5
if Delta >= 0
6
x1=(-b-sqrt(Delta))/(2*a)
7
x2=(-b+sqrt(Delta))/(2*a)
8
else
9
disp("Brak pierwiastków rzeczywistych");
10
end%if
11 else
12
disp("To nie jest równanie kwadratowe");
13 end%if
Należy si˛e zastrzec, że takie sprawdzenie jest tutaj raczej okazja˛ do pokazania
operatora „nierówny” niż praktycznym rozwiazaniem.
˛
Dla bardzo małych a warunek b˛edzie spełniony ale poprawność wyliczonych pierwiastków b˛edzie bardzo
watpliwa.
˛
6
OCTAVE zezwala też na użycie w tym miejscu innej postaci operatora !=, jednak nie jest on
zgodny z Matlabem, wi˛ec dalej b˛edziemy konsekwentnie używali ˜=.
Wersja: 4 października 2009
Rozdz. 3
36
3.7 Koniunkcja i alternatywa.
Rozważmy funkcj˛e
f (x) =


 0 x<0
1 0¬x¬1

 0 x>1
IC
{prog:3:13}
Taka˛ funkcj˛e można napisać na wiele różnych sposobów. Pierwszym jest analogiczny do tego jakiego użyliśmy na str. 27. Jednak ta funkcja ma szczególna˛
cech˛e, poza przedziałem x ∈ [0, 1] w którym jest równa 1, wsz˛edzie jest równa
0. Pozwala to formułować warunek w postaci: jeśli funkcja jest w przedziale to
wartość jest 1 w pozostałych przypadkach wartość jest zero. Tak wi˛ec pomimo, że
formalnie mamy trzy przedziały możemy użyć jednej instrukcji warunkowej. Do
tego potrzeba jednak koniunkcji warunków logicznych, czyli logicznego i.
Operator ten ma postać znaku &.
1 function y=burek(x)
2
if (x>=0)&(x<=1)
3
y=1;
4
else
5
y=0;
6
end%if
7 end%function
SZK
8 burek(-1)
9 burek(1/2)
10 burek(10)
{prog:3:14}
Równie dobrze można powiedzieć, że wartość funkcji jest zero poza przedziałem oraz jeden w przedziale.
Wtedy możnaby użyć operatora negacji, zapisywanego za pomoca˛ znaczka ˜7 .
if ˜( (x>=0)&(x<=1) )
Formalnie jest to poprawnie zapisany warunek i zostanie prawidłowo wyliczony.
Jednak postać jego zapisu jest dość złożona a wi˛ec łatwo prowadzi do bł˛edów.
Warto przeanalizować ten warunek i zapisać go w prostszej postaci.
Jeśli x nie należy do przedziału [0, 1] to należy do przedziału [−∞, 0) lub do
przedziału (1, ∞]. Wtedy warunek musi mieć postać x < 0 lub x > 1.
Prowadzi to do podobnego programu ale musimy wtedy użyć alternatywy czyli
logicznego lub.
Operator alternatywy zapisujemy za pomoca˛ znaczka |.
1 function y=mruczek(x)
2
if (x<0) | (x>1)
3
y=0;
7
OCTAVE akceptuje też alternatywny operator negacji !, który jednak nie jest zgodny z Matlabem.
Dlatego b˛edziemy unikali tej możliwoci.
Wersja: 4 października 2009
Rozdz. 3
37
4
else
5
y=1;
6
end%if
7 end%function
8 mruczek(-1)
9 mruczek(1/2)
10 mruczek(10)
{sec:morgan}
Prawa de Morgana. Warto zwrócić uwag˛e, że pracowicie uzyskane przez nas
uproszczenie warunku logicznego jest w rzeczywistości skorzystaniem z prawa de
Morgana: „Zaprzeczeniem koniunkcji jest alternatywa zaprzeczeń”.
IC
¬((x ­ 0) ∧ (x ¬ 1)) ⇒ ((x < 0) ∨ (x > 1))
Analogicznie, obowiazuje
˛
też prawo: „Zaprzeczeniem alternatywy jest koniunkcja zaprzeczeń”.
¬((x < 0) ∨ (x > 1)) ⇒ ((x ­ 0) ∧ (x ¬ 1))
Znajomość praw de Morgana jest wyjatkowo
˛
przydatna w programowaniu,
gdyż cz˛esto trzeba zamieniać złożone warunki na ich zaprzeczenia.
Skrócona wersja. Warto też zwrócić uwag˛e, że korzystajac
˛ z sekwencyjności
poleceń np. ostatni program można napisać w nieco bardziej zwi˛ezłej postaci:
SZK
1 function y=filemon(x)
2
y=1;
3
if (x<0) | (x>1)
4
y=0;
5
end%if
6 end%function
7 filemon(-1)
8 filemon(1/2)
9 filemon(10)
Wszystkie te programy sa˛ równoważne i nie ma najlepszego. Tak wi˛ec, niekoniecznie istnieje jedna poprawna forma programu.
3.8 Ćwiczenia
1. Napisać funkcj˛e max(a,b), która zwraca wi˛eksza˛ z wartości a i b.
2. Napisać funkcj˛e max3(a,b,c), która zwraca najwi˛eksza˛ z wartości a, b i c.
3. Napisać funkcj˛e amax(a,b), która zwraca wi˛eksza˛ z liczb |a| i |b|.
Wersja: 4 października 2009
{prog:3:15}
Rozdz. 3
38
4. Napisać funkcj˛e amax3(a,b,c), która zwraca najwi˛eksza˛ z wartości |a|, |b|
i |c|.
5. Funkcja atan() zwraca wartość arc tg kata
˛ w przedziale (−π/2, π/2]. Napisać funkcj˛e, która zamienia kat
˛ na przedział [0, π).
6. Napisać funkcj˛e, która jest odcinkowo-liniowa i ma wartość 0 w ±∞ oraz
przechodzi przez punkty: (0, 0), (1, 1) i (2, 0).
7. Napisać funkcj˛e, która poza przedziałem (0, 2) jest tożsamościowo równa
zero. W tym przedziale jest parabola˛ przechodzac
˛ a˛ przez (1, 1). Funkcja jest
wsz˛edzie ciagła.
˛
IC
8. Napisać funkcj˛e, która jest odcinkowo-liniowa i ma wartość 0 w ±∞ oraz
przechodzi przez punkty: (0, 0), (1, 1), (3, 1), (4, 0).
˛ w zmiennej s dany jest sin kata.
˛ Napisać
9. W zmiennej c jest dany cos kata,
program, który określi ćwiartk˛e układu współrz˛ednych.
10. Napisać funkcj˛e, która dla dla dochodu oblicza należny podatek PIT (wedle
obecnie obowiazuj
˛ acych
˛
ustawowych regulacji).
11. Napisać funkcj˛e, która dla danych ocen: w z wykładu i l z laboratorium
zwróci ocen˛e końcowa˛ z przedmiotu TI.
SZK
12. Załóżmy, że jest dost˛epna funkcja psin, która oblicza wartość sin(x) ale
tylko dla argumentów nieujemnych. Napisać funkcj˛e, która, korzystajac
˛ z
nieparzystości funkcji sin oblicza wartości dla dowolnych katów.
˛
Wersja: 4 października 2009