Ta strona wykorzystuje ciasteczka ("cookies") w celu zapewnienia maksymalnej wygody w korzystaniu z naszego serwisu. Czy wyrażasz na to zgodę?

Czytaj więcej
< All Topics
Print

Programowanie warunków

Instrukcja warunkowa

Przy programowaniu wielu zagadnień potrzebna jest selekcja danych względem najróżniejszych warunków. Służą nam do tego wyrażenia warunkowe często spotykane jako elementy składowe wielu funkcji, choć nierzadko wykorzystywane także jako pojedyncze polecenia. Mają one następującą składnię.

if (warunek) instrukcja

lub

if (warunek) {instrukcja.1
instrukcja.2

instrukcja.n}

lub

if (warunek) instrukcja.1 else instrukcja.2

lub

if (warunek) {instrukcja.1.1
instrukcja.1.2

instrukcja.1.n} else {
instrukcja.2.1
instrukcja.2.2

instrukcja.2.m}

Warunek jest wyrażeniem, które przyjmuje wartość TRUE lub FALSE. Instrukcje po nim wykonywane są tylko wtedy, gdy warunek ma wartość TRUE. Gdy dodamy do tej instrukcji else możemy umieścić instrukcje, które maja być wykonane gdy warunek ma wartość FALSE

W powyższych schematach zapisano instrukcje w sposób, który ułatwia dostrzeżenie szczegółów składni pogrubiając słowa kluczowe if i else oraz nawiasy okrągłe i klamrowe. Pogrubienia kodu nie są w programowaniu konieczne. Gdy wykonuje się warunek zapisany w kilku linijkach konieczne jest zapisanie słowa else w tej samej linijce w której zamyka się instrukcje wykonywane gdy warunek ma wartość TRUE. Przerzucenie go do nowej linijki (co robią osoby programujące w C++) powoduje, ze instrukcje po nim nie są wcale wykonywane.

Zapisanie instrukcji warunkowej w konsoli wygląda następująco:

> x = 1
> y = 2
>
> if (x < y) cat("OK\n")
OK
> if (x > y) cat("OK\n")
> 

 

Wersja instrukcji warunkowej bez else powoduje, że gdy warunek ma wartość FALSE instrukcja nie jest wykonywana i możemy nie zobaczyć żadnej reakcji programu.

> x = 1
> y = 2
>
> if (x < y) cat("OK\n") else cat("NOT OK\n") 
OK
> x = 3
> y = 2
> if (x < y) cat("OK\n") else cat("NOT OK\n")
NOT OK

Przykładem zastosowania instrukcji warunkowej jest sprawdzenie czy trzy przykładowe liczby mogą być długościami boków jakiegoś trójkąta. Warunkiem koniecznym takiego zdarzenia jest: suma każdej pary liczb jest większa od trzeciej liczby. To pozwala na wykonanie następującego programu:

> a = 123
> b = 186
> c = 138
> if ((a+b>c) & (a+c>b) & (b+c>a)) {
+ cat("Istnieje trójkąt o bokach",a,", ",b," i ",c,"\n")} else {
+ cat("Nie istnieje trójkąt o bokach",a,", ",b," i ",c,"\n")}
Istnieje trójkąt o bokach 123 , 186 i 138 

Poniżej pokazano program , który można skopiować do skryptu, a który wylicza się pierwiastki rzeczywiste równania kwadratowego ax2+bx+c=0.

a = 1
b = -1
c = -2
delta=b^2-4*a*c
if (delta < 0) cat("brak pierwiastków rzeczywistych\n") else {
x1=(-b-sqrt(delta))/(2*a)
x2=(-b+sqrt(delta))/(2*a)
cat("Są pierwiastki rzeczywiste:  x1 = ",x1,"  x2 = ",x2, "\n")}

Program ten skopiowany do konsoli daje następujący obraz:

> a = 1
> b = -1
> c = -2
> delta=b^2-4*a*c
> if (delta < 0) cat("brak pierwiastków rzeczywistych\n") else {
+ x1=(-b-sqrt(delta))/(2*a)
+ x2=(-b+sqrt(delta))/(2*a)}
> cat("Są pierwiastki rzeczywiste:  x1 = ",x1,"  x2 = 2",x2, "\n")
Są pierwiastki rzeczywiste:  x1 = -1   x2 = 2

 

Funkcja ifelse()

W instrukcji warunkowej warunek nie może być wektorem logicznym (typu (TRUE, TRUE, FALSE,…)). Gdy zatem sprawdzamy warunek dla kolejnych wyrazów wektora i w zależności, czy przyjmuje on wartość TRUE lub FALSE, przekształcamy je inną funkcją, to najlepiej jest zastosować funkcję ifelse(). Jest to funkcja której argumentami są: więc ciąg wartości logicznych, funkcje dla TRUE i dla FALSE rozdzielone przecinkami.

> x=3:-3
> x
[1]  3  2  1  0 -1 -2 -3
> x>0
[1] TRUE TRUE TRUE FALSE FALSE FALSE FALSE
> ifelse(x>0,log(x),exp(-x))
[1] 1.0986123 0.6931472 0.0000000 1.0000000 2.7182818 7.3890561 20.0855369
Warning message:
In log(x) : NaNs produced

Inny przykład: jak zrobić by takie funkcje jak pierwiastek, czy logarytm dawały wartość pomijalną NA zamiast nieokreślonej NaN dla liczb nie należących do ich dziedziny?

> x=3:-3
> x
[1]  3  2  1  0 -1 -2 -3
> sqrt(x)
[1] 1.732051 1.414214 1.000000 0.000000   NaN  NaN  NaN
Warning message:
In sqrt(x) : NaNs produced
> ifelse(x>=0,sqrt(x),NA)
[1] 1.732051 1.414214 1.000000 0.000000   NA   NA   NA
Warning message:
In sqrt(x) : NaNs produced
> sqrt(ifelse(x>=0,x,NA))
[1] 1.732051 1.414214 1.000000 0.000000   NA   NA   NA

Gdy wektor warunków jest jednoelementowy, funkcja ifelse() może zastępować instrukcję warunkową, aczkolwiek z pewnymi problemami. Przykładowo program wyliczający pierwiastki równania kwadratowego postaci:

> a = 1
> b = -1
> c = -2
> delta=b^2-4*a*c
> ifelse (delta < 0, cat("brak pierwiastków rzeczywistych\n"),
+ c((-b-sqrt(delta))/(2*a),(-b+sqrt(delta))/(2*a)))
[1] -1

wyświetla tylko pierwszy z podanych pierwiastków. Aczkolwiek zapis:

> a = 1
> b = -1
> c = -2
> delta=b^2-4*a*c
> ifelse (delta < 0, cat("brak pierwiastków rzeczywistych\n"),
+ x<-c((-b-sqrt(delta))/(2*a),(-b+sqrt(delta))/(2*a)))
[1] -1 
> x
[1] -1  2

powoduje, że ostatecznie x jest wektorem z parą pierwiastków. Uwaga: w funkcji ifelse() nie działa przypisanie za pomocą znaku “=”. Gdy tak zmienimy parametry a, b, c tak, że delta wyjdzie ujemna, uzyskamy komunikat o błędzie. Aby uniknąć takich komunikatów związanych z tym, że funkcja ifelse() nie “lubi” funkcji cat(), lepiej jest zastosować instrukcję warunkową.

 

Funkcja wyboru switch()

Instrukcjami wykonywalnymi pod określonym warunkiem w instrukcji warunkowej mogą być inne instrukcje warunkowe. Oznacza to, że instrukcja if (…) … else … może być wykorzystywana do wykonywania różnych instrukcji zależnych od kilku wartości jakiejś zmiennej. Nie wygląda to jednak przejrzyście.

> a = 1
> if (a == 1) a=4 else {
+ if (a == 2) a=2 else {
+ if (a == 3) a=5 else {
+ if (a == 4) a=3 else a=1}}}
> a
[1] 4

Ta pojedyncza, choć złożona, instrukcja nie może być zastąpiona przez zestaw pięciu instrukcji if (…) …, gdyż wcześniej uzyskane przez a wartości będą rozważane w niżej występujących instrukcjach i ostatecznie wynik będzie błędny.

> a = 1
> if (a == 1) a=4
> if (a == 2) a=2
> if (a == 3) a=5
> if (a == 4) a=3
> if (a > 4) a=1
> a
[1] 3

Rolę wielokrotnego wyboru warunkowego różnych instrukcji pełnią w językach programowania specjalnie skonstruowane instrukcje wyboru warunkowego. zastępują one wielokrotne złożenie funkcji if (…) … else …. W R rolę tę spełnia funkcja switch(), której pierwszym argumentem jest tzw. klucz przyjmujący różne uporządkowane wartości, a pozostałe argumenty rozdzielone przecinkami oznaczają wartości lub instrukcje wykonywane dla kolejnych wartości klucza.

Jedna z działających poprawnie składni z użyciem instrukcji warunkowej i funkcji switch() wygląda następująco:

> a = 1
> if (a==1 | a==2 | a==3 | a==4) switch(a, 4, 2, 5, 3)->a else a=1
> a
[1] 4

Bardziej popularny sposób polega na ujęciu wartości klucza (które może on posiadać na początku) w znaki cudzysłowu i przyporządkowaniu im odpowiednich wartości. Brak wartości w cudzysłowie wskazuje na wykonanie instrukcji alternatywnej. Wygląda to następująco:

> a = 1
> a=switch(as.character(a), "1"=4, "2"=2, "3"=5, "4"=3, 1)
> a
[1] 4

Ta składnia jest najczęściej stosowana. Wymaga jednak zmiany typu klucza na typ tekstowy.

W opracowaniu danych pomocna jest funkcja cut(), która zamienia liczby na przedziały, w których te liczby się znajdują. Została już ona opisana w rozdziale o czynnikach. Umożliwia ona napisanie funkcji, która przyjmuje różne wartości dla liczb należących do kilku różnych przedziałów. Przykładowo, chcemy wyliczyć x2+1 gdy x jest ujemne x+1 gdy x należy do przedziału [0,3] oraz cos(2πx)+3 gdy x jest większe od 3, to możemy zrobić to następująco:

> x = 7.43
> cut(x,c(-Inf,0,3,Inf))
[1] (3, Inf]
Levels: (-Inf,0] (0,3] (3, Inf]
> switch(cut(x,c(-Inf,0,3,Inf)),x^2+1,x+1,cos(2*pi*x)+3)
[1] 2.095173
Warning message:
In switch(cut(x, c(-Inf, 0, 3, Inf)), x^2 + 1, x + 1, cos(2 * pi * :
EXPR is a "factor", treated as integer.
Consider using 'switch(as.character( * ), ...)' instead.
> switch(cut(x,c(-Inf,0,3,Inf)),
+ "(-Inf,0]"=x^2+1,
+ "(0,3]"=x+1,
+ "(3, Inf]"=cos(2*pi*x)+3)
[1] 2.095173
Warning message:
In switch(cut(x, c(-Inf, 0, 3, Inf)), x^2 + 1, x + 1, cos(2 * pi * :
EXPR is a "factor", treated as integer.
Consider using 'switch(as.character( * ), ...)' instead.
> switch(as.character(cut(x,c(-Inf,0,3,Inf))),
+ "(-Inf,0]"=x^2+1,
+ "(0,3]"=x+1,
+ "(3, Inf]"=cos(2*pi*x)+3)
[1] 2.095173

W dwóch pierwszych składniach poza poprawnie wyliczonym wynikiem pojawia się jeszcze komunikat ostrzegawczy. Zastosowanie funkcji as.character() spowodowało wreszcie brak komunikatu o błędzie. W dwóch ostatnich instrukcjach ważne było, by przedział (3,Inf) zapisać nie jako “(3,Inf)” ale “(3, Inf)”, czyli dokładnie tak jak wyglądały wartości czynnika utworzonego przez funkcje cut(). W przeciwnym wypadku funkcja switch() nie wygeneruje żadnego wyniku.

Spis treści