5 . Kapitulua Kontrol-egiturak eta begiztak

Beste lengoaia guztietan bezala, R n kontrol-egitura eta begizta tipikoak erabil ditzakegu. Jarraian, egitura horien sintaxia eta zenbait adibide jasotzen dira.

5.1 if agindua

Exekuzio baldintzatua ahalbidetzen duen egiturarik sinpleenak if eta else if aginduak dira. Funtzio hauek parametro bakarra dute –balio logiko bat itzultzen duen espresio bat, hain zuzen–. Hona hemen agindu hauen sintaxia.

k <- 5
if (k < 2){
  message("k balioa 2 baino txikiagoa da")
}else if (k < 3){
  message("k balioa 2 eta 3 balioen artean dago")
}else{
  message("k balioa 3 edo handiagoa da")
}
## k balioa 3 edo handiagoa da

5.2 switch agindua

if agindua baldintza baten arabera bi kode blokeen artean aukeratzeko erabiltzen da; bi aukera baino gehiago egonez gero, switch agindua erabil dezakegu. Funtzio horren lehenengo parametroa zenbaki edo string bat itzultzen duen espresio bat da. Ondoren, definitutako espresioak itzuli dezakeen balio posible bakoitzari dagozkion kode blokeak datoz. Hona hemen sintaxiaren adibide pare bat.

a <- 1
b <- 2
switch(a+b,
       "A"={
         message("Option A selected")         
       },
       "B"={
         message("Option B selected")         
       },
       "C"={
         message("Option C selected")
       })
## Option C selected
opt <- "B"
switch(opt,
       "A"={
         message("Option A selected")         
       },
       "B"={
         message("Option B selected")         
       },
       "C"={
         message("Option C selected")
       })
## Option B selected

Lehenengo adibidean espresioak zenbaki bat itzultzen du; kode blokeetatik zenbaki horri dagokiona exekutatzen da –emaitza i bada, i. blokea, alegia–. Bigarren adibidean, berriz, espresioaren emaitza string bat da eta beraz izen bereko blokea exekutatuko da. Azken aukera hau erabiltzeko, noski, kode blokeek izena izan behar dute. Berez, zilegi da izenik ez duen bloke bat uztea. Bloke honek kode lehenetsia definituko du: espresioak itzultzen duen stringa ez badago kode blokeen izenen artean, izenik gabeko kode blokea exekutatuko da.

5.3 for agindua

Iterazio kopuru ezaguna dituzten begiztak definitzeko for agindua erabili ohi da. Honetarako, for funtzioaren barruan, aldagaia in zerrenda motako espresio bat sortu beharko dugu; iterazio bakoitzean aldagaiak zerrendako balio bat hartuko du. Hona hemen adibide sinple pare bat.

lst <- c("A", "B", "C", "D", "E")
for (l in lst)
  message("Uneko elementua:", l, "\n")
## Uneko elementua:A
## Uneko elementua:B
## Uneko elementua:C
## Uneko elementua:D
## Uneko elementua:E
s <- 0
for (i in 1:5) {
  s <- s + i
}
s
## [1] 15

5.4 while agindua

Kode bloke jakin bat baldintza bat bete arte behin eta berriro exekutatu behar denean while funtzioa erabiliko dugu. Funtzio horrek logical balio bat itzultzen duen espresio bat du parametro bakartzat. Hona hemen sintaxiaren adibide bat.

i <- 1
while(i < 5) {
  message("i aldagaia txikiegia da (", i, ") ...\n")
  i <- i + 1
}
## i aldagaia txikiegia da (1) ...
## i aldagaia txikiegia da (2) ...
## i aldagaia txikiegia da (3) ...
## i aldagaia txikiegia da (4) ...

5.5 Bestelako aginduak

Ikusi ditugun aginduez gain, badaude kodearen exekuzioa kontrolatzeko erabil daitezkeen beste hiru funtzio, repeat, break eta next. Informazio gehiago lortzeko, ?Control exekuta dezakezu.

5.6 Begiztak eta paralelizazioa

R lengoaian dauden funtzio gehienak bektorialak dira eta, beraz, lengoaia eragiketa bektorialak egiteko dago optimizaturik. Hori agerian gelditzen da jarraian dagoen adibidean.

n <- 1000000
x <- 1:n
y <- n:1

system.time({
  x + y
})
##    user  system elapsed 
##   0.000   0.004   0.004
system.time({
  for (i in 1:n){
    x[i] + y[i]
  }
})
##    user  system elapsed 
##   0.600   0.000   0.601

Adibide honetan bi bektore sortu ditugu, 1etik nrako balio osoak hartzen ditu batek eta ntik 1erakoak besteak. Rn bi bektore hauen osagaiak elementuz elementu batzeko + eragilea erabil dezakegu zuzenean. Beraz, esan dezakegu, Rn batuketa eragilea bektoriala dela. Are gehiago, eragiketa hau, bektoriala izateaz gain paralelizaturik dago, hau da posizio ezberdinetako batuketak aldi berean egiten dira. Beste hainbat legoaiatan bi bektore elementuz elementu batzeko begizta bat erabili beharko genuke, bigarren kode zatian egiten den bezala. Alabaina, Rn hau ez da estrategiarik egokiena, + eragilearen paralelizazioa apurtzen baitugu. Hau honela, adibideko bi kodeek berdina egiten dute baina lehenengoa askoz ere azkarragoa da.

Goiko adibide sinpletik ondorio garrantzitsu bat atera behar dugu: Oro har, Rn programatzerako garaian begiztak saihesten saiatu behar gara. Are gehiago, Rn funtzio asko daude definituta eta optimizatuta eta, beraz, ezer inplementatu aurretik, garatu nahi dugun kodea aurrera eramateko behar ditugun funtzioak iada ez direla existitzen egiaztatzea da egokiena.

R ren oinarrizko funtzio gehienak bektorialki funtzionatzeko daude optimizaturik. Zenbait kasutan ordea, guk sortutako funtzioren bat bektorialki aplikatzeko beharra izango dugu; hau burutzeko apply motako funtzioak erabil ditzakegu. Existitzen diren guztietatik, ondorengo hiruak azalduko ditugu zehaztasun gehiagorekin: sapply, lapply eta apply.

5.6.1 sapply funtzioa

Zerrenda edo bektore baten elementu guztiei funtzio bat aplikatzeko sapply funtzioa erabiltzen da; Emaitza, bektore bat edo matrize bat izango da, aukeratutako funtzioak itzultzen duen emaitza motaren arabera. Adibide moduan, character bektore batek posizio bakoitzean daukan stringaren karaktere kopurua kalkulatuko dugu nchar funtzioa era bektorialean aplikatuz:

chr.vector <- c("Apply", "a", "Function", "over", "a", 
                "List", "or", "Vector")
sapply(X=chr.vector, FUN=nchar)
##    Apply        a Function     over        a     List       or   Vector 
##        5        1        8        4        1        4        2        6

5.6.2 lapply funtzioa

lapply funtzioak sapplyek egiten duen gauza bera egiten du, baina emaitza zerrenda batean gordetzen da matrize batean beharrean. Erabilera berdina da, bakarrik emaitza aldatzen da.

lapply(X=chr.vector, FUN = nchar)
## [[1]]
## [1] 5
## 
## [[2]]
## [1] 1
## 
## [[3]]
## [1] 8
## 
## [[4]]
## [1] 4
## 
## [[5]]
## [1] 1
## 
## [[6]]
## [1] 4
## 
## [[7]]
## [1] 2
## 
## [[8]]
## [1] 6

5.6.3 apply funtzioa

Ikusi ditugun funtzioek bektoreekin eta zerrendekin dihardute, dimentsio bakarreko egiturekin, alegia. Bi dimentsioko egitura bat badugu –matrize edo data.frame bat–, apply funtzioa erabil dezakegu funtzioak bektorialki aplikatzeko. Funtzioaren erabilera antzerakoa da, baina oraingo honetan bi dimentsio ditugunez, beste parametro bat behar dugu, funtzioa ea errenkadaka, zutabeka edo elementuka aplikatu behar den adierazteko. Parametro hau MARGIN da, eta 1 balioa esleitzen badiogu, funtzioa errenkadaka aplikatuko da. Argumentuari 2 balioa esleitzen badiogu, berriz, funtzioa zutabeka aplikatuko da. Azkenik, funtzioa elementuz elementu aplikatu nahi badugu, argumentuari c(1,2) bektorea esleitu beharko diogu.

m <- matrix(1:200, ncol=20)
apply(m, MARGIN=1, FUN=median)
##  [1]  96  97  98  99 100 101 102 103 104 105
apply(m, MARGIN=2, FUN=median)
##  [1]   5.5  15.5  25.5  35.5  45.5  55.5  65.5  75.5  85.5  95.5 105.5
## [12] 115.5 125.5 135.5 145.5 155.5 165.5 175.5 185.5 195.5

Goiko adibidean dauden bi deiek matrize baten mediana konputatzen dute, lehenengo kasuan errenkadaka eta bigarren kasuan zutabeka.

5.6.4 Noiz erabili for eta noiz ez

Oro har, begiztek kodearen eraginkortasunean eragin handia dute, baina eragin hori iterazio kopuruaren araberakoa da; begiztak iterazio gutxi baditu, eragina txikia izango da eta, hortaz, for egiturak erabil daitezke8. Izan ere, kodearen ulergarritasuna dela eta, kasu horietan begiztak erabiltzea komenigarria da, apply motako funtzioak baino errazagoak baitira interpretatzeko.

Tamainaz gain, badago beste aspektu bat apply motako funtzioen eraginkortasuna baldintzatzen duena: sekuentzialtasuna. Kasu batzuetan, kodearen natura sekuentziala izango da, iterazio bakoitzean aurrekoan lortutako emaitza erabili behar dugulako, adibidez. Kasu horietan apply funtzioak lortzen duen paralelizazioa ez da erabilgarria, hortaz, ez dugu ezer irabazten. Hortaz, horrelako kasuetan for egiturak erabiltzea egokia da.

5.7 Ariketak

1. Ariketa Idatzi for begizta bat 1tik 100rako zenbaki bikoiti guztiak idazten dituena. Egin ariketa hau bera begiztarik erabili gabe.

2. Ariketa Idatzi funtzio bat, maximo izenekoa, begizta bat erabiliz bektore bateko balio maximoa bilatu eta itzultzen duena. Begiztarik erabili gabe, nola egingo zenuke hau?

3. Ariketa Definitu honako matrizea Rn: \[\begin{equation} A_{m,n} = \begin{pmatrix} 1 & 3 & 1 & 4 \\ 2 & 1 & 7 & 6\\ 5 & 3 & 1 & 1 \\ 3 & 3 & 7 & 2 \end{pmatrix} \end{equation}\]

Sortu matrize honen zutabe guztien batezbestekoak gordetzen dituen bektore bat for begizta bat erabiliz. Egin ariketa berbera apply erabiliz. Errepikatu ariketa Rko oinarrizko funtzio bat erabiliz.

4. Ariketa Egin funtzio bat zenbaki bat lehena den konprobatzen duena: lehena bada “x zenbakia lehena da” esaldia pantallaratuko du terminalean eta bestela, “x zenbakia ez da lehena”, kasu bakoitzean, x dagokion zenbakiarekin ordezkatuz.

5. ariketa Definitu funtzio bat, argumentu bezala zenbaki natural bat n jasoko duena, eta Fibonacci-ren segidako lehenengo n zenbakiak gordetzen dituen bektore bat itzultzen duena. Erabili while agindua. (OHARRA: Fibonnaciren segida ezagutzen ez baduzue, begiratu bere definizioa Wikipedian).

6. ariketa Definitu zerrenda bat, posizio bakoitzean honako bektore bat duena:

  • \(x=(1,2,3)\)$
  • \(y=(7,5,1,4)\)$
  • \(z=(1,2,1,2,1)\)$
  • \(t=(a,b,c,d,e)\)$

Erabili for begizta bat zerrenda horretako elementu bakoitzaren luzera lortzeko. Egin ariketa bera, lapply komandoa erabiliz.


  1. Are gehiago, kasu batzuetan begiztak eraginkorragoak izan daitezke.