Principis
Programari
Un cop carregat un programari, la memòria principal d'un ordinador conté instruccions i dades. La CPU executa instruccions màquina, que són instruccions de molt baix nivell que processen dades.
El programari s'expressa habitualment utilitzant un llenguatge de programació, una abstracció que amaga el funcionament de la CPU basat en instruccions màquina.
Un programari es pot descriure pel seu comportament i el seu estat:
- El comportament es defineix amb un flux d'instruccions depenent de les dades.
- Les instruccions poden:
- Fer assignacions, o sigui, modificar l'estat
- Bifurcar-se segons condicions (expressions lògiques) a partir de l'estat
- Llegir dades externes i incorporar-les a l'estat
- Escriure dades utilitzant l'estat
El paradigma més habitual de la programació és l'imperatiu. Aquest, utilitza sentències per a modificar l'estat d'un programa.
Codi
La programació estructurada és una característica dels llenguatges de programació que millora la claredat i qualitat del codi. En tenim:
- Control de flux (if/then/else)
- Repetició (while/for)
- Blocs o àmbits de codi
- Subrutines: procediments i funcions
Dades
Les dades poden ser de diferents tipus:
- Primitius: el tipus més bàsic. Numèrics, caràcters, booleans.
- Estructures: més d'una dada. Per exemple, un array o una llista.
- Referències: apuntadors a altres dades (normalment estructures) utilitzant la seva adreça de memòria.
Operacions per a una dada:
- Reservar espai en memòria (també anomenat instanciar).
- Assignar el seu valor (instrucció d'assignació).
- Obtenir el seu valor.
- Eliminar la dada, alliberant l'espai. Pot ser automàtic (Garbage Collection).
Una variable és un nom simbòlic que s'associa a una dada continguda en memòria.
L'assignació d'una variable sol tenir el format variable = expressió. Això implica avaluar l'expressió i assignar el valor a la variable, reemplaçant el valor anterior.
La dada associada a una variable pot ser mutable o immutable. És mutable si pot ser modificada, i immutable si no. Habitualment, els primitius són immutables.
Funcions
Les funcions són agrupacions de codi reutilitzables que podem cridar utilitzant paràmetres. Aquests paràmetres tenen un àmbit reduït al cos de la funció. Poden tenir un valor de retorn.
Les funcions poden:
- Tenir efectes secundaris: si es modifica l'estat fora del seu àmbit. Per exemple, modificant una variable d'un àmbit més global, o un paràmetre mutable. Això és possible si utilitzem referències.
- Ser pures, si la funció sempre retorna el mateix valor per als mateixos paràmetres i no té efectes secundaris.
Les crides a funcions es gestionen utilitzant una espai de memòria anomenat stack. Cada cop que es fa una crida, creem un nou frame i l'afegim a dalt del stack. A dalt del stack sempre hi ha el frame actiu. Durant la seva execució, aquest frame guarda les dades que s'utilitzen. Quan es fa return, s'elimina aquest frame, i el de sota passa a ser l'actiu.
Objectes
Alguns llenguatges permeten classes, que són plantilles per a crear instàncies d'objectes. Un objecte està compost de:
- Codi que s'executa mitjançant mètodes o operacions (funcions de l'objecte).
- Estat, o dit d'una altra forma, les seves dades associades, habitualment privades.
Un objecte que té operacions que poden modificar el seu estat es diu que és mutable. Si en canvi no es pot, l'objecte és immutable.
Quan un objecte no té comportament, només dades, se l'anomena estructura de dades. Les seves operacions exposen aquestes dades.
Memòria
Tenim dos espais de la memòria principal on es guarden dades: el stack (pila) i el heap (munt).
Un frame del stack s'utilitza dins de l'àmbit d'una funció o mètode per a guardar paràmetres, primitius i referències.
El heap s'utilitza per a guardar estructures, ja que les estructures poden sobreviure l'àmbit on es creen mitjançant les seves referències. En canvi, com que els primitius no es poden referenciar, es creen al stack i que moren amb el return.
Expressions
Una expressió aritmètica té un valor numèric. Es forma utilitzant operadors aritmètics sobre variables, constants o funcions numèriques.
Una expressió lògica té dos possibles valors: True o False. Es formen utilitzant variables i constants lògiques utilitzant operadors lògics (NOT, AND, OR...) i relacionals (=, <, >, <=, >=, <>).
Parèntesis i prioritat
Els operadors de les expressions s'apliquen en funció de la seva prioritat.
Per als aritmètics (de més a menys prioritat):
- ++, --
- *, /, %
- +, -
Per als lògics (de més a menys prioritat):
- NOT
- AND
- OR
- operadors relacionals
Es pot prioritzar una operació lògica o aritmètica utilitzant parèntesis ().
Lleis booleanes
- Commutativa:
- a OR b = b OR a
- a AND b = b AND a
- Identitat:
- a OR False = a
- h AND True = h
- Complement:
- a OR NOT(a) = True
- a AND NOT(a) = False
- Idempotent:
- a OR a = a
- a AND a = a
- Doble negació:
- NOT(NOT(a)) = a
- Associativitat:
- a OR b OR c = (a OR b) OR c = a OR (b OR c)
- a AND b AND c = (a AND b) AND c = a AND (b AND c)
- Distributiva:
- a AND (b OR c) = (a AND b) or (a AND c)
- a OR (b AND c) = (a OR b) AND (a OR c)
- De Morgans:
- NOT(a AND b) = NOT(a) OR NOT(b)
- NOT (a OR b) = NOT(a) AND NOT(b)
- Absorció:
- a AND (a OR b) = a
- a OR (a AND b) = a
Paradigmes
Un paradigma de programació és una forma d'estructurar i representar un programa. Cada llenguatge de programació pot suportar un o més paradigmes.
Aquests són alguns dels paradigmes més presents als llenguatges:
- Imperatiu: el codi té un flux d'execució amb sentències que poden canviar l'estat (és mutable). Aquest concepte s'ha perfeccionat amb altres paradigmes com l'estructurat o el procedural, afegint procediments, variables locals, seqüències, iteració, etc.
- Orientat a objecte: estil procedural que amaga (encapsula) l'estat darrere del concepte d'objecte. Els objectes exposen mètodes que manipulen aquest estat.
- Basat en esdeveniments: estil procedural on el flux d'execució és controlat per l'emissió d'esdeveniments, que poden arribar a qualsevol consumidor. Sol haver una cua al sistema que els despatxa.
- Basat en missatges: similar als esdeveniments, però el que s'envia són missatges, i es fa cap a una adreça concreta. Sol haver una cua al receptor per poder gestionar-los quan estigui preparat.
- Asíncron: en contraposició a la programació síncrona, s'executen tasques, habitualment de forma simultània, sense esperar que es completin per a prosseguir. Associat a conceptes com callbacks, promeses, futurs o async/await.
- Reactiu: programació declarativa basada en esdeveniments asíncrons i fluxos de dades. Es basen en APIs d'operacions on els observadors tenen callbacks per gestionar l'èxit i l'error. Els fluxos poden ser pull i push (back pressure).
- Funcional: les operacions (o funcions) són matemàtiques i no hi ha estat. Les funcions permeten altres funcions com a paràmetres, i totes les dades són immutables. L'objectiu és no tenir efectes secundaris al codi.
- Orientat a dades: estil alhora procedural i funcional. El codi modela únicament les dades, que són immutables. Les operacions estan completament separades de les dades.
- Declaratiu: el codi defineix una lògica, però no diu res respecte del flux d'execució.
- Multi-fil: se'n diu de la possibilitat de dissenyar programes amb més d'un fil d'execució treballant coordinadament.
A continuació es mostren alguns llenguatges de programació els paradigmes principals que els defineixen (tot i permetre altres):
- Java: procedural, orientat a objecte, multi-fil
- Python: procedural, orientat a objecte
- JavaScript: procedural, asíncrona, basat en esdeveniments
- SQL: declaratiu
Conceptes addicionals
Concurrència
La concurrència és l'habilitat d'un sistema de poder executar diverses tasques alhora. En una aplicació es pot implementar de diverses maneres. Per exemple, amb multi-fil. Veure el capítol sobre concurrència.
Mutabilitat
Es diu que una dada és mutable si pot ser modificada després de ser creada. Les dades immutables són més eficients i més útils. En programació concurrent es diu que són inherentment thread-safe, o sigui, no impliquen conflictes quan s'accedeixen per més d'un fil alhora.
Recursivitat
La recursivitat és una forma de resoldre problemes on la solució depèn de la solució a un problema més petit. La forma habitual en la qual la resolen els llenguatges de programació és permetent que una funció es pugui cridar a sí mateixa.
Efectes secundaris
Un efecte secundari es produeix quan una operació fa alguna cosa més que llegir els seus paràmetres i retornar un valor a qui l'ha cridada. Per exemple: llegir una variable no local, canviar un paràmetre mutable, llençar un error o fer E/S. D'una funció sense efectes secundaris se'n diu pura.