dissabte, 6 de maig del 2017

Aprendre a programar 0: Entorn de desenvolupament i variables

Recordo quan vaig fer l'assignatura de Programació 1. És una assignatura que em va encantar, però era, probablement, la que més costava a la gent aquell quadrimestre. Normalment ningú ha fet una assignatura així. Això, sumat a que les transparències que teníem estan molt resumides i en anglès, i que era l'assignatura que més gent es saltava, feia que molta gent suspengués. Jo, per això, he pensat que, aprofitant que va ser l'assignatura que em va anar millor, i que no vaig tenir gaire problemes amb ella, podria intentar ajudar a la gent. Tenint en compte els percentatges d'aprovats que està tenint l'assignatura (que són bastant baixos) aquest blog serà molt útil
2015-16: 54,12%
2016-17: 43,12%
2017-18: 56,57%

Si algú considera que hi ha coses que no queden clares, que falta informació, o té qualsevol suggerència/crítica/carta bomba, estic obert a tot. M'ho podeu comentar als comentaris, o, si sou estudiants de la FIB, em podeu enviar un mail. Sabent que em dic Oriol Martí i Rodríguez, no us costarà descobrir-lo. No us recomano comentar perquè segurament no ho veuré, però la possibilitat hi és

Per altra banda, aquest blog és completament per ajudar. No pretenc guanyar diners amb ell, no el tinc monetitzat ni posaré cap PayPal ni res d'això. Accepto birres al bar si algú em vol convidar, això sí.
El blog tampoc pretén ser un substitut de les classes. De fet, la meva recomanació és que aneu tant a les classes de teoria com a les classes de laboratori. Teniu l'Aula Lliure com a reforç que us pot ajudar moltíssim, i aquest blog que espero que també. Per a dubtes puntuals podeu aprofitar les consultes, quedeu amb el professor i ell us resoldrà els dubtes. Com a últim recurs, està l'opció de fer classes fora de la FIB, per a qui tingui la sort de poder-les pagar. Jo us recomano no recorrer a les acadèmies, ja que el model que promouen acaba sent "pan para hoy, hambre para mañana", i enlloc d'això, buscar-vos un alumne de cursos avançats que us faci algunes classes. Jo mateix en puc fer si algú les necessita, i aquestes sí que les cobraria (si em feu preguntes puntuals pel WhatsApp o el mail, no, però si he de dedicar-li una estona sí), però tampoc pretenc vendre'm com el profe perfecte o l'únic possible. Qualsevol alumne amb primer aprovat hauria de poder-vos fer classes de reforç

Tots els problemes que resolc, o la gran majoria, són del Jutge. No poso l'enllaç, però poso quin és el codi del problema i, si no és públic, poso també captures o alguna manera de veure l'enunciat. Si sou estudiants de la FIB esteu registrats. I, si no ho sou, us podeu registrar, és completament gratis


Comencem pel principi: L'entorn de desenvolupament

Per programar, fan falta dues coses. Primera, un editor de text. I segona, un compilador. Normalment, a la FIB s'utilitza el Kate com a editor de text, i com a compilador, el g++. El llenguatge de programació que s'utilitza és C++. 
Jo, no obstant, faig servir un editor de text que es diu Geany. L'avantatge que té és que està tant per Windows com per Linux. El prefereixo davant d'altres com el Dev C++, perquè als examens també es pot fer servir. No obstant, això dels editors de text i compiladors és una mica qüestió de gustos. Si no vols/pots instal·lar res, sempre pots fer servir aquest online. No sé si serà accessible pels examens o no, ni l'he fet servir, però té molt bona pinta. 


Quan programes, el que fas és escriure tot el codi. El deses en un arxiu .cc, o .cpp (preferiblement cc), i el compiles. Per compilar, jo recomano fer-ho des de la terminal. Seria fent
$ g++ codi_entrada.cc
Si ho fas així (el dòlar indica que és una comanda, no s'ha de posar), et genera un arxiu a.out, que és l'executable del teu codi. El programa, però, té altres opcions
$ g++ codi_entrada.cc -o nom_executable
Això el que fa és que l'executable es digui nom_executable. Per executar-lo, només cal executar
$ ./nom_executable
Veureu molta gent posar una extensió a aquest executable (.exe en general, o alguns .x). No cal cap extensió, en els sistemes Unix o basats en Unix, no cal que els fitxers tinguin extensió, el propi sistema identifica el seu tipus sense ella. Com que l'únic que vols és provar el programa per saber si va o no, te la pots estalviar sense cap problema

A l'assignatura de PRO1, es fan servir una sèrie de flags que serveixen per detectar errors. I, normalment, voldrem compilar amb ells. Podem copiar-los cada cop, fent 
$ g++ -D_JUDGE_ -DNDEBUG -O2 -Wall -Wextra -Werror -Wno-uninitialized -Wno-sign-compare -Wshadow codi_entrada.cc -o nom_executable

No obstant, això és molt llarg de fer cada vegada. Per això, es pot definir un àlies. Suposant que estàs a Linux, faries
$echo $SHELL
I mires la sortida. Si és /bin/bash, vol dir que tens l'intèrpret bash. Si és /bin/tcsh, tens l'intèrpret tcsh. 

Si tens bash:

$alias p1++="g++ -D_JUDGE_ -DNDEBUG -O2 -Wall -Wextra -Werror -Wno-uninitialized -Wno-sign-compare -Wshadow"

Si tens tcsh:

Has d'editar el fitxer ~/.tcshrc i afegir al final aquesta línia:
alias p1++ g++ -D_JUDGE_ -DNDEBUG -O2 -Wall -Wextra -Werror -Wno-uninitialized -Wno-sign-compare -Wshadow

Per fer-ho, pots fer
$ geany ~/.tcshrc
I quan s'obri el fitxer, afegir-ho. Si no, també pots fer
$ echo "alias p1++ g++ -D_JUDGE_ -DNDEBUG -O2 -Wall -Wextra -Werror -Wno-uninitialized -Wno-sign-compare -Wshadow" >>~/.tcshrc
I això ho afegeix directament

Un cop hagis definit l'àlies, tanques la consola, i ja el tens definit. Per compilar només hauràs de fer
$p1++ codi_entrada.cc -o nom_executable

Els flags aquests poden canviar, així que quan ho facis assegura't de que són els correctes. Els pots trobar al Jutge, i segur que als documents de l'assignatura també

El primer programa

#include <iostream>
using namespace std;
int main () {
    //Aquí, el programa
}

Com podeu observar, he posat una mena de plantilla. Es quedarà curta al cap de poc temps, però per començar, ens servirà. Una cosa que heu de saber, és que en un programa en C++, hi ha dues maneres de fer comentaris. La primera és 
//comentari d'una sola línia
la segona 
/* Comentari que pot ocupar
    tantes línies com ens sembli */
Tot el que hi hagi en un comentari, seran coses que el compilador ignorarà. Ens servirà per donar indicacions i deixar coses més clares, tot i que tampoc se n'ha d'abusar. 

Aleshores, un programa senzill seria

#include <iostream>
using namespace std;
int main () {
    cout <<"Hola mon!"<<endl;
}

Aquest programa el que faria és mostrar el text "Hola món" per pantalla. 
Com podem veure, l'instrucció que hem posat acaba amb un ;. Totes les instruccions acaben amb un punt i coma, per indicar que la instrucció ha acabat
- Per escriure per pantalla:
    cout <<v1<<v2<<v3...
Això el que fa és escriure per pantalla el que hi ha a la variable v1, després el de v2, després el de v3... Sense espais ni separacions. Si vols mostrar un text, l'únic que has de fer és posar-lo entre cometes dobles ("). Per acabar la línia, li fas mostrar un endl, i això farà que passi a la següent línia
-Per llegir per l'entrada estàndard:
    cin >>v1>>v2>>v3;
Això llegirà el que tu li escriguis, i ho desarà a les variables v1, v2 i v3

Espera, què és una variable?

Variables:

Una variable és un nom per representar una zona de memòria amb informació. Te les pots imaginar com una capsa amb informació.

Una variable té un tipus. Hi ha molts tipus diferents, però a PRO1 només se'n fan servir uns pocs. No obstant, jo els posaré tots, i en negreta, els que es fan servir a l'assignatura


Aquí sembla que tinguin mides molt definides, però res més lluny de la veritat. L'estàndard defineix que
  • Un bool té dos valors possibles, true o false. No obstant, com que el byte és la unitat mínima que pot adreçar un processador, un booleà ha de tenir mida 1 byte
  • Un char té mida 1 byte com a mínim. Com que l'estàndard defineix que un byte, a C++ té 8 o més bits, tindrà de mida 8 bits com a mínim
  • Un short té una mida major que un char. 
  • Un int té mida major o igual que un short
  • Un long té mida major o igual que un int
  • Un long long és necessàriament major que un long
Per tant, amb això, podem veure que la mida d'un short, un int i un long pot ser igual. O poden tenir mida diferent els tres. Si en algun moment es necessita determinar la mida exacta, es poden utilitzar int32_t si vols que tingui signe, o uint32_t si no. Pots fer-ho amb 8, 16, 32 o 64 bits. Uns quants exemples:

int hola; //Declarem una variable entera que es diu hola
int tres=3; //Declarem una variable entera que es diu tres, i val 3
unsigned int u;//Declarem una variable entera sense signe. Aquí, posar int és opcional
long long l;//Declarem una variable de tipus long long
char a='a'; //Un caràcter. El seu contingut és la lletra a
unsigned char byte=255;//Declarem una variable entera de 8 bits
double penetration;//Ouch
int dos=2, tres=3; //Aquesta no acostuma a agradar gaire als profes

Curiositat: El temps, segons l'estàndard POSIX, es representa comptant els segons en una variable entera de 32 bits. Això s'aplica als sistemes UNIX, i, gràcies a que C és un llenguatge molt utilitzat, en molts altres sistemes. Com ja hem vist, un enter de 32 bits pot representar valors des de -2^31 fins a 2^31-1. Això són 
unes 596.523 hores, que són uns 24.855 dies, és a dir, una mica més de 68 anys. Com que es comença a comptar des del dia 1 de gener de 1970 a les 00:00:00, això fa que l'últim moment representable sigui el 19 de gener de 1938 a les 03:14:07. Si no hem canviat de sistema, arribat aquest moment, el comptador saltarà al valor -2^31, i viatjarem enrere en el temps. Això es coneix com el problema de l'any 2038, o Y2K38.
La sort és que, si hem completat la migració als sistemes de 64 bits, tenint en compte que aquests utilitzen variables de 64 bits pel temps, tindríem uns 2,9 billons d'anys (això és gairebé un 3 seguit de 12 zeros, un número prou gran com per no preocupar-nos)

Operador d'assignació

Aquest és l'operador que s'utilitza per donar valor a una variable. Un exemple seria
int tres=3;
Això crearia una variable que es diu tres, i té valor 3.
Podem donar valor també de la següent manera, però aquesta no s'acostuma a utilitzar en tipus bàsics
int tres(3);
Ho poso per si trobeu algú que la utilitza, però ni se us acudeixi utilitzar-la

Operadors lògics

Aquests s'acostumen a utilitzar per avaluar condicions. Totes retornen un valor booleà. Hi ha una sèrie d'operadors bastant obvis, que són
==: serveix per comparar dues variables. Si són iguals, retorna true. Si no, false
!=: serveix per comparar dues variables. Si són diferents, retorna true. Si no, false
<: serveix per comparar dues variables. Equival al "menor que" matemàtic
>: serveix per comparar dues variables. Equival al "major que" matemàtic
<=: serveix per comparar dues variables. Equival al "menor o igual que" matemàtic
>=: serveix per comparar dues variables. Equival al "major o igual que" matemàtic

and: Retorna true si les dues condicions són certes. False en altres casos. Equival a la disjunció matemàtica, o al producte en àlgebra booleana
or: Retorna false si les dues condicions són falses. True en altres casos. Equival a la conjunció matemàtica, o a la suma en àlgebra booleana
not: Nega una condició. Si és true, retorna false. Si és false, retorna true

En C, la and lògica s'escrivia com a &&, la or lògica com a ||, i la not lògica com a !. De la mateixa manera, els valors booleans no són true i false, sinó que són 1 i 0. Per tant, les expressions de sota són equivalents
a and b
a && b

a or b
a || b

not a
!a

a and not b
a && !b

bool cert=true;
bool cert=1;

En C++ es poden utilitzar les dues opcions. Jo utilitzaré les que explico aquí, però signifiquen exactament el mateix. Hi ha gent que fins i tot les barreja. 
Aleshores, la taula de la veritat dels operadors seria aquesta

Operadors matemàtics 

Aquí trobem les operacions matemàtiques més típiques. Suma, resta, producte, divisió entera, mòdul... 
Els símbols són:
Suma: +
Resta: -
Multiplicació: *
Divisió: /
Mòdul (o residu): %

I compte! La potència no és ^. Aquesta és una altra operació que no hi té res a veure

S'ha d'anar amb compte amb les divisions i mòduls, ja que si un dels operands és negatiu, el resultat podria no ser el que esperem

Aleshores podríem fer un programa com el següent:
int main () {
    int a, b;//Podem declarar tantes variables com vulguem, separant-les per comes
    cin >>a>>b;
    cout <<"El resultat de la divisio es "<<a/b<<endl;
    cout <<"El resultat del modul es "<<a%b<<endl;
}

Hi ha altres operadors matemàtics molt útils, que tot i que no cal dominar per aprovar l'assignatura, està bé conèixer-los. Són els següents:
a+=b
a-=b
a*=b
a/=b
a%=b
++a
--a
a++
a--

Els 5 primers vindrien a ser una forma abreviada. Enlloc de posar a=a+b, podem posar a+=b. Ho podem llegir com "suma-li b a a". 
El ++a és el preincrement. El que fa és incrementar el valor de a. És a dir, podríem dir que equival a fer a+=1, tot i que ho hauríem de dir fluixet, perquè no ens sentin gaire
a++ és un postincrement. Fa el mateix, incrementar el valor de a. No obstant, té una petita diferència (no cal saber-la per fer l'assignatura, fins a EDA no es tracta), explicada a sota. 
--a i a-- vindrien a ser el mateix, però decrementant. 



Diferència entre preincrement i post increment (si no t'interessa, salta-la)

Podem fer
a=++b;

a=b++;

I no són el mateix. La primera incrementa b, i li dóna aquest valor a a. La segona primer li dóna el valor, i després la incrementa. És a dir, mirant-ho amb números, si tens que b val 3
a=++b;
b passa a valdre 4. a també
a=b++;
b passa a valdre 4. a passa a valdre 3

És a dir, la diferència és que un s'incrementa abans d'avaluar-se, i l'altre després

Operadors bit a bit

Aquests te'ls pots saltar també. No es fan servir a PRO1, però trobo que són interessants. 
&: És la and bit a bit. Seria aplicar l'operació and a tots els bits. També  pot escriure's com a bitand
Un exemple: les variables tenen 8 bits. a=3, b=21
Com que 3 en binari és 00000011, i 21 en binari és 00010101, agafaríem els bits i aplicaríem la and
00000011
00010101
I dóna
00000001, que és posar a 1 els bits que estan a 1 a la variable a, i la b. 
|: És la or bit a bit. Equivalent a la &, però posant a 1 els bits que estan a 1 a la variable a, o a la b. Pot ser també bitor
^: És la xor bit a bit, també coneguda com a "or exclusiva". Posa a true els bits on només un dels operands és cert. També es pot escriure xor
~: És la not bit a bit, també coneguda com a complement. Canvia el valor de tots els bits. Podem escriure també compl
<<: Desplaçament lògic. a<<b desplaça tots els bits de la variable a b posicions cap a l'esquerra. Això en números sense signe equival a multiplicar per 2^b. 3<<2, per exemple, donaria 12. 
>>: Desplaçament lògic també, però cap a l'altra banda. En nombres sense signe equival a dividir entre 2^b. 64>>2 donaria 16

Per últim, hi ha un operador que realment no és bit a bit, però és interessant. És l'operador ?:
a?b:c el que faria és que si a és cert, s'avalua com a b. Si no, com a c. Per exemple, un programa com el següent

int main () {
    int a, b;
    cin >>a>>b;
    cout <<(a>b?a/b:b/a)<<endl;
}

Aquest programa el que fa és mostrar el resultat de dividir el més gran entre l'altre.

int main () {
    int a, b;
    cin >>a>>b;
    ++(a<b?a:b);
    cout <<a<<' '<<b<<endl;
}

Aquest incrementa el número més petit

En el següent article us parlaré d'estructures de control, i podrem començar a fer programes divertits i amb alguna utilitat