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