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

Pętla iteracyjna for

Składnia pętli for (…) {…}

W R bardzo dużo można zrobić wpisując pojedyncze polecenia, ale nie jest to zbyt wydajny sposób pracy. Pewne operacje na danych wykonuje się wielokrotnie podczas jednej analizy. Żeby nie wpisywać określonego polecenia dziesiątki czy setki razy używa się pętli. Najpopularniejsza z nich to pętla for (…) {…}. Może mieć ona następującą konstrukcję:

for ( iterator in wektor ) instrukcja

albo

for ( iterator in wektor ) { instrukcja }

albo

for ( iterator in wektor ) { instrukcja 1
instrukcja 2

instrukcja n}

Stałe elementy tej instrukcji to słowa kluczowe for i in, nawiasy okrągłe oraz nawiasy klamrowe dla wielolinijkowych instrukcji. Za iterator można wstawić dowolny literał, podobnie wektor może (ale nie musi) być zdefiniowany wcześniej i można wstawić tu jego nazwę. Instrukcje mogą mieć dowolny charakter (przypisania, funkcje, instrukcje warunkowe, pętle, w tym także pętle “for”). Mogą, ale nie muszą wykorzystywać chwilową wartość iteratora. Jedyne ograniczenie, to brak możliwości zmiany wartości iteratora wewnątrz nawiasów klamrowych przez jakieś przypisanie, np iterator=2 lub iterator=f(iterator).

Iteratorem jest jakiś literał, który przybiera kolejne wartości wektora. Instrukcje wykonywane są dla kolejnych wyrazów wektora wyszczególnionego w nawiasie, a więc powtarzają się tyle razy, ile jest wyrazów w tym ciągu. Instrukcje te mogą, ale nie muszą one wykorzystywać wartości wyrazów wektora, czyli chwilowej wartości iteratora.

W informatyce przyjęto rozpoczynanie nauki programowania od wyświetlania na ekranie napisu “Hello word!”. W R skromnym odpowiednikiem tej czynności jest zastosowanie funkcji cat():

> cat("Hello World!")
Hello World!> cat(Hello world!\n")
Hello World!

Dopisywanie “\n” na koniec tekstu powoduje przejście kursora do nowej linijki. Żeby wydrukować ten napis dziesięciokrotnie można oczywiście tyleż razy przywołać to polecenie w konsoli R, ale będzie to żmudne zadanie. Lepszym rozwiązaniem jest użycie pętli for:

> for (i in 1:10) cat("Hello World!\n")
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
>

Iterator w instrukcji for nie zostaje umieszczony w katalogu obiektów R. Ma on tylko wartości wewnątrz pętli. Można ten fakt wykorzystać w następujący sposób:

> for (i in 1:10) cat("To jest napis nr", i, "\n") 
To jest napis nr 1
To jest napis nr 2
To jest napis nr 3
To jest napis nr 4
To jest napis nr 5
To jest napis nr 6
To jest napis nr 7
To jest napis nr 8
To jest napis nr 9
To jest napis nr 10
>

 

Funkcje zastępujące w R pętlę iteracyjną

Wiele instrukcji, dla których w językach programowania C++, Pascal, Java itp., należałoby tworzyć pętle, w R wykonywanych jest za pomocą jednej funkcji. Skraca to znacznie kod źródłowy i powoduje, że jest on bardziej czytelny. Pętle są jednak w R możliwe. Przykładowo, wyliczanie średniej wartości ciągu liczb można wykonać za pomocą funkcji mean() lub za pomocą pętli “for”.

> ciag=c(3, 4, 5, 2, 4, 3, 4, 2, 1, 6)
> srednia=0
> for (i in 1:length(ciag)) srednia=srednia+ciag[i]
> srednia=srednia/length(ciag)
> srednia
[1] 3.4
> mean(ciag)
[1] 3.4

Bardziej złożone funkcje umożliwiają uniknięcie podwójnej pętli, którą należałoby użyć np. w C++ do wyliczania sum wyrazów w wierszach jakiejś macierzy liczbowej. Taką podwójną pętlę zastępuje funkcja apply(macierz,1,sum), gdzie za słowo macierz podaje się nazwę analizowanej macierzy, liczba 1 na drugiej pozycji oznacza, że chodzi o zastosowanie funkcji sum dla wierszy. W R wyliczenie sum z wierszy macierzy można zrealizować na trzy sposoby:

> mat=matrix(1:12,4,3)
> mat
    [,1] [,2] [,3]
[1,]    1    5    9
[2,]    2    6   10
[3,]    3    7   11
[4,]    4    8   12
> sumy=rep(0,nrow(mat))
> for (i in 1:nrow(mat)) for (j in 1:ncol(mat)) sumy[i]=sumy[i]+mat[i,j]
> sumy
[1] 15 18 21 24
> sumy=NULL
> for (i in 1:nrow(mat)) sumy=c(sumy,sum(mat[i,]))
> sumy
[1] 15 18 21 24
> sumy=apply(mat,1,sum)
> sumy
[1] 15 18 21 24

Przy wyliczaniu sum jakiś wymiarów w macierzy wielowymiarowej zastąpienie np. potrójnej pętli za pomocą specjalnej funkcji nie jest już możliwe. Czasami także łatwiej jest “ogarnąć” problem obliczeniowy używając pętli niż posługiwać się gotowymi w R funkcjami, których ograniczeń nie jesteśmy pewni. Przykładem może być problem z wyliczenie wszystkich osób, które przeżyły lub nie, katastrofy Titanica z podziałem na status (klasa podróży lub załoga), ale nie zależnie od wieku i płci uczestników podróży. Podstawowe dane znajdują się w czterowymiarowej tablicy Tytanic zaimplementowanej do R. Obliczenia można zrealizować w następujący sposób:

> Titanic
, , Age = Child, Survived = No
#
Sex
Class  Male Female
1st     0      0
2nd     0      0
3rd    35     17
Crew    0      0
#
, , Age = Adult, Survived = No
#
Sex
Class  Male Female
1st   118      4
2nd   154     13
3rd   387     89
Crew  670      3
#
, , Age = Child, Survived = Yes
#
Sex
Class  Male Female   1st     5      1
2nd    11     13
3rd    13     14
Crew    0      0
#
, , Age = Adult, Survived = Yes
#
Sex
Class  Male Female
1st    57    140
2nd    14     80
3rd    75     76
Crew  192     20
#
> #Pierwszy wymiar to status, drugi – Płeć, trzeci – wiek, czwarty – Przeżycie
> sumy=outer(rep(0,length(Titanic[,1,1,1])),rep(0,length(Titanic[1,1,1,])))
> dimnames(sumy)[[1]]=dimnames(Titanic)[[1]] > dimnames(sumy)[[2]]=dimnames(Titanic)[[4]] > for (i in 1:length(Titanic[,1,1,1])) {
+ for (j in 1:length(Titanic[1,1,1,])) sumy[i,j]=sum(Titanic[i,,,j])}
> sumy
No Yes
1st  122 203
2nd  167 118
3rd  528 178
Crew 673 212

 

Przykłady stosowania pętli for (…) {…}

Przykładem konieczności stosowania pętli, której nie można już zastąpić jedną funkcją istniejącą w podstawowych bibliotekach R, jest wyliczanie wartości ciągów rekurencyjnych. Poniższy przykład pokazuje sposób wykreślania przebiegu ciągu xn, gdzie xn+1=xn+xn*(−0.01*xn+2.6) oraz x0=10. Jest to przykład modelu dynamiki liczebności populacji – tzw. modelu logistycznego w czasie dyskretnym.

Program ten zapisany w skrypcie wygląda następująco:

a=-0.01
b=2.6
x=rep(10,100)
for (i in 2:100) x[i]=x[i-1]+x[i-1]*(a*x[i-1]+b)
x

Po przeniesieniu do konsoli uzyskamy następujące wyniki:

> a=-0.01
> b=2.6
> x=rep(10,100)
> for (i in 2:100) x[i]=x[i-1]+x[i-1]*(a*x[i-1]+b)
> x
[1] 10.0000 35.0000 113.7500 280.1094 223.7811 304.8321 168.1694 322.6004
[9] 120.6513 288.7774 205.6748 317.4080 135.1903 303.9209 170.4360 323.0853
[17] 119.2660 287.1138 209.2664 315.4348 140.5742 308.4561 158.9904 319.5860
[25] 129.1576 298.1505 184.4046 323.8060 117.1984 284.5595 214.6730 311.9778
[33] 149.8186 314.8908 142.0446 309.5939 156.0542 318.2660 132.8252 301.7454
[41] 175.7807 323.8220 117.1524 284.5018 214.7938 311.8939 150.0399 315.0239
[49] 141.6854 309.3199 156.7636 318.6007 131.8985 300.8625 177.9226 323.9568
[57] 116.7643 284.0124 215.8142 311.1735 151.9353 316.1237 138.7034 306.9459
[65] 162.8474 321.0579 125.0267 293.7794 194.5425 321.8852 122.6860 291.1510
[73] 200.4544 319.8162 128.5144 297.4924 185.9555 323.6453 117.6602 285.1375
[81] 213.4611 312.8036 147.6321 313.5232 145.7156 312.2458 149.1105 314.4584
[89] 143.2094 310.4645 153.7901 317.1304 135.9525 304.5982 168.7530 322.7350
[97] 120.2671 288.3198 206.6683 316.8880

Inny przykład to wypisanie początkowych wyrazów ciągu Fibonacciego. Jest to ciąg xn taki, że x0=1, x1=1, …, xn=xn-2+xn-1. Gdy chcemy wypisać wszystkie jego wyrazy do n=100, to najlepiej jest zrobić następujący program:

x=c(1,1)
for (i in 2:100) x=c(x,x[i-1]+x[i])
x

Po przeniesieniu do konsoli uzyskujemy ciąg liczb zapisanych w formacie naukowym. Możemy je wyświetlić dokładnie używając funkcji format() z opcją scientific=FALSE.

> x=c(1,1)
> for (i in 2:100) x=c(x,x[i-1]+x[i])
> x
 [1] 1.000000e+00 1.000000e+00 2.000000e+00 3.000000e+00 5.000000e+00 8.000000e+00
[7] 1.300000e+01 2.100000e+01 3.400000e+01 5.500000e+01 8.900000e+01 1.440000e+02
[13] 2.330000e+02 3.770000e+02 6.100000e+02 9.870000e+02 1.597000e+03 2.584000e+03
[19] 4.181000e+03 6.765000e+03 1.094600e+04 1.771100e+04 2.865700e+04 4.636800e+04
[25] 7.502500e+04 1.213930e+05 1.964180e+05 3.178110e+05 5.142290e+05 8.320400e+05
[31] 1.346269e+06 2.178309e+06 3.524578e+06 5.702887e+06 9.227465e+06 1.493035e+07
[37] 2.415782e+07 3.908817e+07 6.324599e+07 1.023342e+08 1.655801e+08 2.679143e+08
[43] 4.334944e+08 7.014087e+08 1.134903e+09 1.836312e+09 2.971215e+09 4.807527e+09
[49] 7.778742e+09 1.258627e+10 2.036501e+10 3.295128e+10 5.331629e+10 8.626757e+10
[55] 1.395839e+11 2.258514e+11 3.654353e+11 5.912867e+11 9.567220e+11 1.548009e+12
[61] 2.504731e+12 4.052740e+12 6.557470e+12 1.061021e+13 1.716768e+13 2.777789e+13
[67] 4.494557e+13 7.272346e+13 1.176690e+14 1.903925e+14 3.080615e+14 4.984540e+14
[73] 8.065155e+14 1.304970e+15 2.111485e+15 3.416455e+15 5.527940e+15 8.944394e+15
[79] 1.447233e+16 2.341673e+16 3.788906e+16 6.130579e+16 9.919485e+16 1.605006e+17
[85] 2.596955e+17 4.201961e+17 6.798916e+17 1.100088e+18 1.779979e+18 2.880067e+18
[91] 4.660047e+18 7.540114e+18 1.220016e+19 1.974027e+19 3.194043e+19 5.168071e+19
[97] 8.362114e+19 1.353019e+20 2.189230e+20 3.542248e+20 5.731478e+20
> format(s, scientific=FALSE)
  [1] "                    1" "                    1" "                    2"
[4] "                    3" "                    5" "                    8"
[7] "                   13" "                   21" "                   34"
[10] "                   55" "                   89" "                  144"
[13] "                  233" "                  377" "                  610"
[16] "                  987" "                 1597" "                 2584"
[19] "                 4181" "                 6765" "                10946"
[22] "                17711" "                28657" "                46368"
[25] "                75025" "               121393" "               196418"
[28] "               317811" "               514229" "               832040"
[31] "              1346269" "              2178309" "              3524578"
[34] "              5702887" "              9227465" "             14930352"
[37] "             24157817" "             39088169" "             63245986"
[40] "            102334155" "            165580141" "            267914296"
[43] "            433494437" "            701408733" "           1134903170"
[46] "           1836311903" "           2971215073" "           4807526976"
[49] "           7778742049" "          12586269025" "          20365011074"
[52] "          32951280099" "          53316291173" "          86267571272"
[55] "         139583862445" "         225851433717" "         365435296162"
[58] "         591286729879" "         956722026041" "        1548008755920"
[61] "        2504730781961" "        4052739537881" "        6557470319842"
[64] "       10610209857723" "       17167680177565" "       27777890035288"
[67] "       44945570212853" "       72723460248141" "      117669030460994"
[70] "      190392490709135" "      308061521170129" "      498454011879264"
[73] "      806515533049393" "     1304969544928657" "     2111485077978050"
[76] "     3416454622906707" "     5527939700884757" "     8944394323791464"
[79] "    14472334024676220" "    23416728348467684" "    37889062373143904"
[82] "    61305790721611584" "    99194853094755488" "   160500643816367072"
[85] "   259695496911122560" "   420196140727489664" "   679891637638612224"
[88] "  1100087778366101888" "  1779979416004713984" "  2880067194370816000"
[91] "  4660046610375530496" "  7540113804746346496" " 12200160415121876992"
[94] " 19740274219868225536" " 31940434634990100480" " 51680708854858326016"
[97] " 83621143489848426496" "135301852344706760704" "218922995834555203584"
[100] "354224848179261997066" "573147844013817200640"

Ostatni przykład pokazuje sposób szyfrowania tekstu kodem Cezara. Wymaga on klucza, który można zmieniać co jakiś czas za obopólną wiedzą. Osoby korespondujące ze sobą umawiają się, że będą używać w korespondencji małych i dużych liter alfabetu, polskich liter, cyfr arabskich, pauzy, kropki, przecinka i znaku zapytania. Decydują się na porządek alfabetyczny stosowanych znaków tak, by przypisywać im liczby stanowiące pozycję w tym ciągu. Klucz jest ustalonym teksem stosującym te znaki.

Podczas szyfrowania poszczególne znaki tekstu i klucza zostają zamienione na liczby – numer miejsca w stosowanym “alfabecie”. Ciąg liczbowy związany z kluczem zostaje wielokrotnie powtórzony tak, aby miał długość tekstu. Poszczególne liczby z tekstu i klucza są dodawane do siebie. Gdy uzyskana liczba przekracza liczbę stosowanych znaków, wielkość ta zostaje zmniejszona o liczbę stosowanych znaków. Następnie uzyskane liczby są znowu zamienione na tekst. Ten tekst przesyła się odbiorcy. Bez znajomości klucza trudno go rozszyfrować.

Programy szyfrujący i deszyfrujący napisane w skrypcie R wyglądają następująco:

##Szyfrowanie tekstu kodem Cezara.
#
##Ustalony zestaw stosowanych znaków:
alfabet=c(letters,LETTERS,as.character(c(0,1,2,3,4,5,6,7,8,9)),
c("ą","Ą","ć","Ć","ę","Ę","ł","Ł","ń","Ń","ó","Ó","ś","Ś","ż","Ż","ź","Ź"," ",".",",","?"))
alfabet=sort(alfabet)
#
##Ustalony klucz i jego szyfr
klucz="informatyka"
szyfrkl=NULL
for (i in 1:nchar(klucz)) szyfrkl=c(szyfrkl,which(alfabet==substr(klucz,i,i)))
#
##Przesyłany tekst i jego szyfrowanie
tekst="Król Karol kupił królowej Karolinie korale koloru koralowego."
szyfr=NULL
for (i in 1:nchar(tekst)) szyfr=c(szyfr,which(alfabet==substr(tekst,i,i)))
szyfr=szyfr+szyfrkl
szyfr=ifelse(szyfr>length(alfabet),szyfr-length(alfabet),szyfr)
szyfrtekst=paste(alfabet[szyfr],collapse="")
szyfrtekst #Tekst wysyłany odbiorcy
#
##deszyfrowanie tekstu "szyfrtekst"
szyfr=NULL
for (i in 1:nchar(szyfrtekst)) szyfr=c(szyfr,which(alfabet==substr(szyfrtekst,i,i)))
deszyfr=szyfr-szyfrkl
deszyfr=ifelse(deszyfr<0,deszyfr+length(alfabet),deszyfr)
cat(alfabet[deszyfr],"\n",sep="")

 

Program ten można zmodyfikować zmieniając zestaw stosowanych znaków, sposób ich uporządkowania oraz oczywiście klucz.

Spis treści