7 Programmazione condizionale e cicli
Finora abbiamo eseguito tutte le nostre operazioni in maniera sequenziale, ovvero i comandi venivano risolti uno dopo l’altro. A volte può succedere che si voglia eseguire un comando o un insieme di comandi solo se si verifica una certa condizione oppure vogliamo eseguire lo stesso comando un numero predefinito di volte (for) oppure ancora fino a che una certa condizione non si verifica (while).
7.1 if else
Nel primo caso avremo bisogno di utilizzare la programmazione condizionale, ovvero lo statement if(<condizione>){<comandiTRUE>}
. Se dovremo dire a R cosa fare quando una certa condizione viene rispettata allora useremo solo l’if
, altrimenti, se dobbiamo specificare anche l’altra strada, cioè quando la condizione non viene rispettata, dovremo usare anche l’else{<comandiFALSE>}
.
La <condizione>
va definita in termini di operazione logica, ovvero deve produrre come risultato un solo TRUE
o un FALSE
.
Se la condizione avrà valore TRUE
R eseguirà il codice in <comandiTRUE>
, se anche l’else è stato specificato allora, in caso la condizione abbia valore FALSE
, R eseguirà il codice in <comandiFALSE>
.
Ad esempio, possiamo usare l’if
per rendere positivo un numero negativo:
if (num < 0) {
<- num * -1
num }
Se num
è positivo R non eseguirà alcun comando, in caso contrario lo moltiplicherà per -1 rendendolo positivo.
Tra le parentesi graffe è possibile scrivere un numero indefinito di righe di codice. Proviamo a scrivere la funzione precedente aggiungendo la funzione print("<testo>")
per comunicare con l’utente:
<- -1
num if (num < 0) {
print("num è negativo.")
print("Non ti preoccupare, lo sistemo io.")
<- num * -1
num print("Ora num è positivo.")
}
## [1] "num è negativo."
## [1] "Non ti preoccupare, lo sistemo io."
## [1] "Ora num è positivo."
num
## [1] 1
Esercizio 18
Cosa ritornerà il codice seguente?
<- 1
x if (3 == 3) {
<- 2
x
} x
Un 2. All’inizio x
è uguale a 1, siccome <condizione>
è sempre uguale a TRUE
, R
eseguirà i comandi all’interno della graffa e trasformerà x
in 2.
Esercizio 19
Cosa ritornerà il codice seguente?
<- 1
x if (x == 1) {
<- 2
x if (x == 1) {
<- 3
x
}
} x
Ancora un 2. Questa volta la seconda condizione non viene rispettata e quindi R non esegue i comandi tra le parentesi del secondo if.
Per definire i comandi da eseguire nel caso la condizione non sia verificata possiamo usare lo statement if(<condizione>){<comandiTRUE>}else{<comandiFALSE>}
.
Partiamo con un numero decimale:
<- 3.14 a
Isoliamo la parte decimale:
<- a - trunc(a)
dec dec
## [1] 0.14
Ora usiamo if else
per arrotondare a
:
if (dec >= 0.5) {
<- trunc(a) + 1
a else {
} <- trunc(a)
a
}
a
## [1] 3
Se si hanno più casi da analizzare possiamo annidare i costrutti di programmazione condizionale if else
usando lo statement else if(){}
.
Ad esempio, se vogliamo confrontare il risultato di due giocatori a
e b
dobbiamo considerare i 3 casi possibili: vincita, perdita o parità.
<- 1
a <- 1
b
if (a > b) {
print("A vince!")
else if (a < b) {
} print("B bince!")
else {
} print("parità")
}
## [1] "parità"
7.2 cicli for
Per ripetere un pezzo di codice un numero predefinito di volte, una per ogni elemento di un vettore di indici, possiamo usare il ciclo for
.
Se ad esempio si vuole eseguire un comando 4 volte si può usare il comando seguente:
for (indice in c("My", "first", "for", "loop")) {
print("un giro")
}
## [1] "un giro"
## [1] "un giro"
## [1] "un giro"
## [1] "un giro"
indice
assumerà i valori nel vettore c("My", "first", "for", "loop")
e per questo R eseguirà il codice tra parentesi graffe una volta per ognuno dei valori nel vettore. La variabile indice
è disponibile all’interno del ciclo per essere usata a proprio piacimento, ad esempio:
for (indice in c("My", "first", "for", "loop")) {
print(indice)
}
## [1] "My"
## [1] "first"
## [1] "for"
## [1] "loop"
La variabile indice
risiede nel Global environment per cui è accessibile anche al di fuori del ciclo for
e avrà come valore l’ultimo assunto nel ciclo. In questo caso "loop"
. A differenza dell’ambiente function, le variabili create all’interno di un ciclo sono disponibili nel Global environment.
Esercizio 20
Scrivere una funzione che, attraverso il ciclo for, calcoli il numero di assi in una mano da 10 carte estratte a caso.
<- estraiCarte(deck, 10)
mano <- 0
nAssi for (carta in 1 : nrow(mano)){
if(mano$figura[carta] == "asso"){
<- nAssi + 1
nAssi
}
} mano
## figura seme valore id
## 19 otto fiori 8 19
## 14 re fiori 13 14
## 31 nove quadri 9 31
## 18 nove fiori 9 18
## 42 jack cuori 11 42
## 36 quattro quadri 4 36
## 30 dieci quadri 10 30
## 47 sei cuori 6 47
## 22 cinque fiori 5 22
## 35 cinque quadri 5 35
nAssi
## [1] 0
Si noti che il codice precedente è equivalente a
<- sum(mano$figura == "asso") nAssi
Esercizio 21
Creare una funzione che calcoli la somma del valore delle carte in una mano di blackjack. Si usi il mazzo BJdeck
creato nel capitolo precedente e la funzione per estrarre carte estraiCarte(mazzo, nCarte)
.
<- estraiCarte(BJdeck, 3)
mano <- function(mano) {
calcolaSomma <- nrow(mano)
nCarte <- 0
nAssi <- 0
somma for(carta in 1 : nCarte){
if(mano$figura[carta] != "asso"){
<- somma + mano$valore[carta]
somma else{
}<- nAssi + 1
nAssi
}
}#cat("la somma dei valori senza gli assi è ", somma,"\n")
if(nAssi > 0){
for(asso in 1 : nAssi){
if((somma + 11) > 21){
<- somma + 1
somma else{
}<- somma + 11
somma
}
}
}return(somma)
}calcolaSomma(mano)
## [1] 18
7.3 while
Se non abbiamo idea di quante volte dobbiamo eseguire un certo comando possiamo usare lo statement while(<condizione>){<comandiTRUE>}
. Qusto statement ci permette di eseguire <comandiTRUE>
finchè la <condizione>
non diventa FALSE
. Il test sulla condizione viene fatto in entrata del ciclo, cioè prima di eseguire <comandiTRUE>
. Se la condizione non è più verificata, il ciclo while
viene interrotto.
Attenzione! Non eseguire MAI il ciclo while(TRUE){}
poichè la condizione rimarrà sempre vera e il ciclo non si interromperà MAI. In tal caso basta premere il bottone STOP nella console per bloccare l’esecuzione.
Proviamo a stampare “ciao” nella console fino a che l’utente non dirà di smettere. Per svolgere questo esercizio avrete bisogno del codice seguente:
<- readline(prompt = "<messaggio>") risposta
Tramite questo comando possiamo assegnare a risposta
ciò che l’utente digita nella console.
Quindi possiamo procedere scrivendo il codice necessario per svolgere l’operazione richiesta:
<- "y"
continua while(continua != "smetti"){
print("ciao")
<- readline(prompt = 'Se vuoi smettere scrivi "smetti": \n')
continua }
Esercizio 22
Quale sarà il valore di conteggio
e di somma
alla fine di questo comando?
= 0
somma = 1
conteggio while (conteggio < 10){
= somma + conteggio
somma = conteggio + 1
conteggio }
conteggio
sarà uguale a 10, mentre somma
sarà uguale a 45
Esercizio 23
Supponiamo di avere una giovane coppia che ha chiesto un prestito di 300.000 euro a una banca a un interesse annuo del 5%. L’ammortamento è in 30 anni e la rata deve essere pagata mensilmente. La coppia decide di pagare il prestito con una rata di 1.600 euro. Riusciranno a pagare il mutuo in 30 anni? Usare R per simulare il pagamento e provare a rispondere alla domanda.
# set up
<- 0 # conta il numero di mesi
nMesi <- 300000 # capitale iniziale
capitale <- 1600 # rata mensile
rata <- 0.05 # tasso di interessa annuale
tasso <- 0 # quanto pagato finore
pagato
# Covnertire il tasso annuale in mensile e trasformarlo in moltiplicatore (1+tasso)
<- (1+tasso) ^ (1/12)
tassoM
# eseguire i comandi finchè il capitale non è esaurito
while ( capitale > 0 ) {
# calcoli del mese
<- nMesi + 1
nMesi <- capitale * tassoM # aggiungi interessi
capitale <- capitale - rata # paga la rata
capitale <- pagato + rata # totale pagato finora
pagato
# stampare in console il bilancio mensile = print()
# cat( "mese", nMesi, ": capitale", round(capitale), "\n")
# end of loop
}
# pagamenti totali:
cat("pagamento totale:", pagato, "\n" )
## pagamento totale: 569600
cat("in mesi: ",nMesi)
## in mesi: 356
Esercizio supplementare avanzato
Creare una funzione che simuli una partita di blackjack senza scommesse. Un solo giocatore contro il banco. Queste le regole:
- Un mazzo da 52 carte.
- Si mescola il mazzo (shuffle).
- All’inizio sia il banco che la propria mano sono vuote (
data.frame()
). - Si estraggono 2 carte, la prima la si da al giocatore, la seconda al banco.
- Si rivela la carta del giocatore. Si chiede al giocatore se vuole continuare a giocare (“y”) oppure se vuole uscire (“n”).
- Si estrae una carta e la si aggiunge al banco.
- Si calcolano le somme dei valori delle carte (calcolaSomma) sia nel banco che nella mano.
- Se il giocatore ha deciso di continuare viene estratta un’altra carta e aggiunta alla mano. Si continua con i passi 5/6/7 fino a che il giocatore o il banco non hanno sforato (hanno superato il 21) o il giocatore non esce dal gioco (al passo 5 sceglie “n”).
- Se il banco ha un valore totale uguale o più alto del valore della mano del giocatore o il giocatore ha sforato, il banco vince. Altrimenti, vince il giocatore.
Per eseguire il passo 5 usare le seguenti righe di codice:
#1. preparazione del mazzo, aggiungiamo a "BJdeck" una colonna di identificatori "id=1:52". Ci servirà per ricordare quali carte sono già state estratte
$id <- 1:52
BJdeck= function(mazzo){
BJPartita<- mazzo
BJdeckPartita #2. mischia il mazzo
shuffle(BJdeckPartita)
#start the game
#3.
<- data.frame()
mano <- data.frame()
banco <- TRUE
continuaWhile while(continuaWhile){
#4.
<- estraiCarte(BJdeckPartita, 2)
carte #togliamo le carte estratte dal mazzo
<- BJdeckPartita[-carte$id, ]
BJdeckPartita #assegniamo le carte
<- rbind(mano, carte[1,])
mano <- rbind(banco, carte[2,])
banco <- calcolaSomma(mano)
sommaMano <- calcolaSomma(banco)
sommaBanco #5.
cat("\n \n la tua mano è: \n")
print(mano[,c(1,2)],row.names = FALSE)
if(sommaMano > 21){ #il giocatore ha sforato
cat("Hai perso! \n")
<- FALSE # esco dal while, finisce la partita
continuaWhile else{
}<- readline(prompt="Continuare? (y = sì, n = no)") #comparirà nella console
continua while(!(continua %in% c("y","n"))){ #finchè il giocatore non decide se continuare o meno
<- readline(prompt="Comando non riconosciuto. Continuare? (y = sì, n = no)")
continua
}#ora "continua" contiene il carattere "y" o "n"
if(continua=="n"){
if((sommaBanco < sommaMano) | sommaBanco >21){ #la mano è maggiore del banco o il banco ha sforato!
cat("Hai vinto! \n" )
else{cat("Hai perso! \n")}
}<- FALSE # esco dal while, finisce la partita
continuaWhile
}cat("Le carte del banco: \n")
print(banco[, c(1, 2)], row.names = FALSE)
}
}
}#per avviare la partita digitare BJparita(BJdeck) nella console