llenguatge informàtic utilitzat per controlar el comportament d'una màquina From Wikipedia, the free encyclopedia
Un llenguatge de programació és un llenguatge informàtic utilitzat per controlar el comportament d'una màquina, normalment un ordinador. Cada llenguatge té una sèrie de regles sintàctiques i semàntiques estrictes que cal seguir per escriure un programa informàtic, i que en descriuen l'estructura i el significat respectivament. Aquestes regles permeten especificar tant la classe de dades amb què treballarà el programa com les accions que realitzarà. Mentre que alguns llenguatges es defineixen per una especificació formal (un document), altres són definits oficiosament per una implementació concreta (un compilador).
Actualment existeixen milers de llenguatges de programació, i se'n creen de nous contínuament.[1] Alguns dels més extensos són ASM, C, C++, C# i Java, tot i que els llenguatges de programació relacionats amb la web, com ara PHP i Javascript estan esdevenint més i més populars.
Els llenguatges se solen classificar principalment en llenguatges de baix nivell, que són molt propers al codi binari utilitzat internament per un tipus d'ordinador determinat, i llenguatges d'alt nivell, que són més propers al llenguatge humà i més independents del tipus d'ordinador.
La diferència entre llenguatges de baix i alt nivell es fa evident comparant dos programes que escriuen "Hola" en pantalla, el primer usant llenguatge d'assemblador per màquines x86 (baix nivell),
MODEL SMALL
IDEAL
STACK 100H
DATASEG
HW DB 'Hola!$'
CODESEG
MOV AX, @data
MOV DS, AX
MOV DX, OFFSET HW
MOV AH, 09H
INT 21H
MOV AX, 4C00H
INT 21H
END
i el segon utilitzant un llenguatge d'alt nivell (Python),
print("Hola!")
Els llenguatges d'alt nivell es poden classificar també segons el tipus de model de programació que utilitzen (procedimentals, funcionals, orientats a objectes, etc.). També hi ha llenguatges de marcatge, que no són llenguatges de programació, sinó que s'utilitzen per estructurar o donar forma a un text. Avui en dia existeixen gran quantitat de llenguatges, més o menys especialitzats en diferents tasques.
Tots els llenguatges de programació tenen alguns elements de formació primitius per a la descripció de les dades i dels processos o transformacions aplicades a aquestes dades (tal com la suma de dos nombres o la selecció d'un element que forma part d'una col·lecció). Aquests elements primitius són definits per regles sintàctiques i semàntiques que descriuen la seva estructura i significat respectivament.
A la forma visible d'un llenguatge de programació se la coneix com a sintaxi. La majoria dels llenguatges de programació són purament textuals, és a dir, utilitzen seqüències de text que inclouen paraules, nombres i puntuació, de manera similar als llenguatges naturals escrits. D'altra banda, hi ha alguns llenguatges de programació que són més gràfics en la seva naturalesa, utilitzant relacions visuals entre símbols per especificar un programa.
La sintaxi d'un llenguatge de programació descriu les combinacions possibles dels símbols que formen un programa sintàcticament correcte. El significat que se li dona a una combinació de símbols és manejat per la seva semàntica (ja sigui formal o com parteix del codi dur de la referència d'implementació). Ja que la majoria dels llenguatges són textuals, aquest article tracta de la sintaxi textual.
La sintaxi dels llenguatges de programació és definida generalment utilitzant una combinació d'expressions regulars (per a l'estructura lèxica) i la Notació De Backus-naur (per a l'estructura gramatical). Aquest és un exemple d'una gramàtica simple, obtinguda de Lisp:
expressió ::= àtom |llista
àtom ::= nombre|símbol
nombre ::= [+-]?['0'-'9']+
símbol ::= ['A'-'Z''a'-'z'].*
llista ::= '(' expressió* ')'
Amb aquesta gramàtica s'especifica el següent:
Alguns exemples de seqüències ben formades d'acord amb aquesta gramàtica:
'12345
', '()
', '(a b c232 (1))
'
No tots els programes sintàcticament correctes són semànticament correctes. Molts programes sintàcticament correctes tenen inconsistències amb les regles del llenguatge; i poden (depenent de l'especificació del llenguatge i la solidesa de la implementació) resultar en un error de traducció o execució. En alguns casos, tals programes poden exhibir un comportament indefinit. A més, fins i tot quan un programa està ben definit dins d'un llenguatge, encara pot tenir un significat que no és el que la persona que ho va escriure estava tractant de construir.
Usant el llenguatge natural, per exemple, pot no ser possible assignar-li significat a una oració gramaticalment vàlida o l'oració pot ser falsa:
El següent fragment en el llenguatge C és sintàcticament correcte, però executa una operació que no està definida semànticament (ja que p és un punter nul, les operacions p→real i p→im no tenen cap significat):
complex *p = NULL;
complex abs_p = sqrt (p→real * p→real + p→im * p→im);
Si la declaració de tipus de la primera línia fos omesa, el programa dispararia un error de compilació, perquè la variable "p" no estaria definida. Però el programa seria sintàcticament correcte encara, ja que les declaracions de tipus proveeixen informació semàntica només.
La gramàtica necessària per especificar un llenguatge de programació pot ser classificada per la seva posició en la Jerarquia de Chomsky. La sintaxi de la majoria dels llenguatges de programació pot ser especificada utilitzant una gramàtica Tipus-2, és a dir, són gramàtiques lliures de context. Alguns llenguatges, incloent Perl i a Lisp, contenen construccions que permeten l'execució durant la fase d'anàlisi. Els llenguatges que permeten construccions que permeten al programador alterar el comportament d'un analitzador fan de l'anàlisi de la sintaxi un problema sense decisió única, i generalment enfosqueixen la separació entre anàlisi i execució. En contrast amb el sistema de macros de Lisp i els blocs Begin de Perl, que poden tenir càlculs generals, les macros de C són mers reemplaçaments de cadenes, i no requereixen execució de codi.
El sistema de tipus defineix com un llenguatge de programació classifica els valors i expressions en tipus, com els manipula, i com hi interacciona. Generalment això inclou la descripció de les estructures de dades que el llenguatge pot construir. El disseny i estudi matemàtic d'aquests sistemes és conegut com la Teoria de tipus (type theory).
S'ha de recordar però que els computadors digitals moderns emmagatzemen internament totes les estructures simplement com a uns i zeros (binari).
Un llenguatge és tipat si l'especificació de cada operació defineix a quins tipus de dades una operació és aplicable, amb la implicació que no és aplicable als altres tipus.[2] Per exemple, "aquest text entre cometes" és un string
. En la majoria de llenguatges de programació, dividir un número per un string
no té sentit. En conseqüència la majoria dels llenguatges de programació moderns rebutjaran qualsevol intent de dur a terme una operació com aquesta. En alguns llenguatges, l'operació sense sentit es detectarà en temps de compilació (comprovació de tipus "estàtica") i serà rebutjada pel compilador. Mentre que en d'altres, l'operació sense sentit es detectarà en temps d'execució (comprovació de tipus "dinàmica"), resultant en una excepció en temps d'execució (runtime exception
).
Un cas especial de llenguatges tipats són els llenguatges mono-tipats. Normalment són llenguatges de script o de marcatge, com Rexx o SGML, i només tenen un tipus de dades - majoritàriament, cadenes de caràcters que són utilitzats tant per dades simbòliques com numèriques.
En contrast, un llenguatge no tipat, com la majoria de llenguatges assembladors, permet qualsevol operació sobre qualsevol tipus de dades, considerades com a seqüències de bits de mides vàries. Llenguatges d'alt nivell no tipats inclouen BCPL i algunes varietats de Forth.
A la pràctica, mentre que pocs llenguatges es consideren tipats des del punt de vista de la Teoria de tipus (verificar o rebutjar totes les operacions), la majoria del llenguatges moderns ofereixen un cert grau de tipatge. Molts llenguatges de producció ofereixen alguna manera de subvertir o fer un bypass al sistema de tipus.
En els sistemes de tipatge estàtics totes les expressions tenen els seus tipus determinats abans que el programa s'executi els (normalment al moment de compilar). Per exemple, 1 i (2+2) són expressions d'enters; no es poden passar a una funció que espera una cadena, o emmagatzemar-se en una variable definida per contenir dates.
Els llengües estàticament tipats poden ser o bé tipats de forma manifestada o bé tipats de forma inferida. En el primer cas, el programador ha d'escriure explícitament els tipus en certes posicions textuals (per exemple, en les declaracions de variables). En el segon cas, el compilador infereix els tipus de les expressions i les declaracions basant-se en el context.
El tipatge dinàmic, també anomenat tipat latent, determina el tipus de les operacions en temps d'execució; en altres paraules, els tipus estan associats amb valors de temps d'execució més que no pas a expressions textuals.
Els llenguatges feblement tipats permeten que un valor d'un tipus pugui ser tractat com d'un altre tipus; per exemple una cadena pot ser operada com un nombre. Això pot ser útil a vegades, però també pot introduir certs tipus de fallades que no poden ser detectades durant la compilació o de vegades ni tan sols durant l'execució.
Els llenguatges fortament tipats eviten que passi l'anteriorment explicat: qualsevol intent de dur a terme una operació sobre el tipus equivocat dispara un error. Als llenguatges amb tipus forts se'ls sol anomenar de tipus segurs.
Llenguatges amb tipus febles com Perl i JavaScript permeten un gran nombre de conversions de tipus implícites. Per exemple en JavaScript l'expressió 2 * x
converteix implícitament x
a un nombre, i aquesta conversió és reeixida inclusivament quan x
és null
, undefined
, un Array
o una cadena de lletres. Aquestes conversions implícites són útils amb freqüència, però també poden ocultar errors de programació.
Les característiques d'estàtics i forts són ara generalment considerades conceptes ortogonals, però el seu tractament en textos de l'àmbit de la programació és divers. Alguns utilitzen el terme de tipus forts per referir-se a tipus fortament estàtics o, per augmentar la confusió, simplement com a equivalència de tipus estàtics. De tal manera que C ha estat considerat com a llenguatge tant de tipus forts, com de tipus estàtics febles.
La semàntica estàtica defineix les restriccions sobre l'estructura dels textos vàlids que resulta impossible o molt difícil expressar mitjançant formalismes sintàctics estàndard. Per als llenguatges compilats, la semàntica estàtica bàsicament inclou les regles semàntiques que es poden verificar en el moment de compilar. Per exemple la revisió que cada identificador sigui declarat abans de ser utilitzat (en llenguatges que requereixen tals declaracions) o que les etiquetes a cada braç d'una estructura casi siguin diferents. Moltes restriccions importants d'aquest tipus, com la validació que els identificadors siguin utilitzats en els contextos apropiats (per exemple no sumar un enter al nom d'una funció), o que les trucades a subrutines tinguin el nombre i tipus de paràmetres adequat, poden ser implementades definint-les com regles en una lògica coneguda com a sistema de tipus. Altres formes d'anàlisis estàtiques, com les anàlisis de flux de dades, també poden ser part de la semàntica estàtica. Nous llenguatges de programació com a Java i C# tenen una anàlisi definida d'assignacions, una forma d'anàlisi de flux de dades, com a part de la seva semàntica estàtica.
La implementació d'un llenguatge és la que proveeix una manera que s'executi un programa per a una determinada combinació de programari i maquinari. Existeixen bàsicament dues maneres d'implementar un llenguatge: compilació i interpretació.
També es pot utilitzar una alternativa per traduir llenguatges d'alt nivell. En lloc de traduir el programa font i gravar de forma permanent el codi objecte que es produeix durant la compilació per utilitzar-lo en una execució futura, el programador només carrega el programa font a la computadora junt amb les dades que es processaran. A continuació, un programa intèrpret, emmagatzemat al sistema operatiu del disc, o inclòs de manera permanent dins de la màquina, converteix cada proposició del programa font en llenguatge de màquina així que vagi sent necessari durant el processament de les dades. El codi objecte no es grava per utilitzar-lo posteriorment.
La següent vegada que s'utilitzi una instrucció, se l'haurà d'interpretar una altra vegada i traduir a llenguatge màquina. Per exemple, durant el processament repetitiu dels passos d'un cicle o bucle, cada instrucció del bucle haurà de tornar a ser interpretada en cada execució repetida del cicle, el que fa que el programa sigui més lent en temps d'execució (perquè es va revisant el codi en temps d'execució) però més ràpid en temps de disseny (perquè no s'ha d'estar compilant a cada moment el codi complet). L'intèrpret elimina la necessitat de realitzar una compilació després de cada modificació del programa quan es vol agregar funcions o corregir errors; però és obvi que un programa objecte compilat amb antelació s'haurà d'executar amb molta major rapidesa que un que s'ha d'interpretar a cada pas durant una execució del codi.
La majoria de llenguatges d'alt nivell permeten la programació multipropòsit, tanmateix, molts d'ells van ser dissenyats per permetre programació dedicada, com ho va ser el Pascal amb les matemàtiques al seu començament. També s'han implementat llenguatges educatius infantils com a Logo que mitjançant una sèrie de simples instruccions. En l'àmbit d'infraestructura d'Internet, es pot destacar Perl amb un poderós sistema de processament de text i una enorme col·lecció de mòduls.
Per escriure programes que proporcionin els millors resultats, es pot tenir en compte una sèrie de detalls:
Els programes es poden classificar pel paradigma del llenguatge que s'usi per produir-los. Els principals paradigmes són: programació imperativa, programació declarativa i programació orientada a objectes.
Els programes que usen un llenguatge imperatiu especifiquen un algorisme, usen declaracions, expressions i sentències.[3] Una declaració associa un nom de variable amb un tipus de dada, per exemple: var x: integer;
. Una expressió conté un valor, per exemple: 2 + 2
conté el valor 4. Finalment, una sentència ha d'assignar una expressió a una variable o usar el valor d'una variable per alterar el fluix d'un programa, per exemple: x := 2 + 2; if x == 4 then fes_quelcom();
. Una crítica comuna en els llenguatges imperatius és l'efecte de les sentències d'assignació sobre una classe de variables dites "no locals".[4]
Els programes que usen un llenguatge declaratiu especifiquen les propietats que la sortida ha de conèixer i no especifica qualsevol detall d'implementació. Dues àmplies categories de llenguatges declaratius són els llenguatges funcionals i els llenguatges lògics. Els llenguatges funcionals no permeten assignacions de variables no locals, així, es fan més fàcil, per exemple, programes com funcions matemàtiques.[4] El principi darrere els llenguatges lògics és definir el problema que es vol resoldre (l'objectiu) i deixar els detalls de la solució al sistema.[5] L'objectiu és definit donant una llista de subobjectius. Cada subobjectiu també es defineix donant una llista dels seus subobjectius, etc. Si en tractar de buscar una solució, una ruta de subobjectius falla, llavors tal subobjectiu es descarta i sistemàticament es prova una altra ruta.
La forma en la qual es programa pot ser per mitjà de text o de forma visual. En la programació visual els elements són manipulats gràficament en comptes d'especificar-se per mitjà de text.
Els primers llenguatges de programació són anteriors a l'aparició de l'ordinador modern. Al segle xix hi havia telers programables i rotlles per a pianola que van aplicar el que avui es considera com a exemples de llenguatges de domini específic. Posteriorment, a principis del segle xx van aparèixer les targetes perforades com a mitjà per a codificar i emmagatzemar dades que eren tractades mecànicament. A la dècada del 1930 i 1940, els formalismes d'Alonzo Church amb el seu càlcul lambda i d'Alan Turing amb la màquina de Turing van aportar una abstracció matemàtica per expressar algorismes; el càlcul lambda segueix tenint influència sobre el disseny del llenguatges.[6]
Als anys 40, es van crear els ordinadors digitals elèctrics. El primer llenguatge de programació d'alt nivell dissenyat va ser el Plankalkül, desenvolupat per Konrad Zuse entre el 1943 i 1945. Tanmateix, no es va implementar fins al 1998 i 2000.[7]
Els programadors dels ordinadors de principis dels anys 50 utilitzaven la primera generació de llenguatges, és a dir, la programació es donava en l'únic llenguatge que entenia el microprocessador: el seu propi codi binari, anomenat llenguatge de màquina o codi màquina (1GL). El problema d'aquest llenguatge de màquina és que és molt lent, ja que les dades s'introdueixen en el sistema binari i, a més, s'han de conèixer les posicions de memòria on s'emmagatzemen les dades. És lògic, per tant, veure que aquest tipus de programació pot patir un gran nombre d'errors i la feina de depuració, és a dir, detectar i eliminar els errors, exigeix molt temps i dedicació [8]
És per aquests problemes del llenguatge de màquina, que ràpidament va ser substituït pels llenguatges de segona generació coneguts com a llenguatges assembladors. Aquest codi d'assemblatge (ASSEMBLY) utilitza una sèrie d'abreviatures mnemotècniques per representar les operacions, com ADD (sumar), STORE (copiar)...[8] Més endavant, encara als anys 50, a mesura que els ordinadors es van anar introduint en el món empresarial i acadèmic, el desenvolupament de llenguatge d'assemblador va ser reemplaçat pels llenguatges de "tercera generació" (3GL), com el FORTRAN, el Lisp i COBOL.[9] Els 3GLs són més abstractes i són "portables", o almenys es poden implementar de forma similar als ordinadors amb un codi màquina natiu diferent. Algunes versions actualitzades de 3GLs encara s'utilitzen, i han influenciat el desenvolupament posterior.[10] A finals dels anys 50 es va introduir l'ALGOL 60, del que la major part dels llenguatges posteriors en són descendents en molts aspectes.[10]
Un tret que comparteixen tots els llenguatges de programació és que les ordres donades en qualsevol d'aquests han de traduir-se al codi binari de l'ordinador, que és l'únic que entén la seva unitat central. Aquesta traducció es dona per mitjà d'un intèrpret o un compilador. La major diferència que trobem entre aquests és que, en contrast a l'intèrpret que tradueix les instruccions una per una, el compilador tradueix tot el programa de cop, deixant-lo llest per executar. D'aquesta manera, s'aconsegueix una major rapidesa i s'alliberen recursos de memòria, ja que els intèrprets exigeixen quedar-se residint en la memòria.
Entre els llenguatges que sempre són compilats podem trobar PASCAL, FORTRAN, COBOL...[8]