Redaktion og opdatering af indholdet på denstoredanske.dk er indstillet pr. 24. august 2017. Artikler og andet indhold er tilgængeligt i den form, der var gældende ved redaktionens afslutning.

  • Artiklens indhold er godkendt af redaktionen

program (Programmeringssprog)

Oprindelig forfatter HBHa Seneste forfatter Redaktionen

Programmerne til de tidligste computere blev skrevet i maskinsprog. Disse computere var enten binære (som i dag) eller decimale, hvilket betød, at programmøren måtte nedskrive de binære eller decimale cifre, der repræsenterede hver instruktion internt i computeren. Nedskrivning af lange rækker af binære tal blev allerede dengang anset for at være upraktisk og behæftet med store fejlmuligheder, og derfor noterede man almindeligvis de binære tal på en kortere form, som oktale (grundtal 8) eller heksadecimale (grundtal 16) tal. I det oktale talsystem går der tre binære cifre på ét oktalt ciffer, og i det heksadecimale system er der fire binære cifre pr. heksadecimalt ciffer; der er altså en direkte sammenhæng med de binære tal. I begyndelsen af 1950'erne indså man, at denne omstændelige fremgangsmåde ikke var nødvendig, men at programmøren kunne specificere instruktionskoder og lageradresser som symbolske mnemotekniske koder, der kunne oversættes til det interne maskinsprog vha. et program kaldet en assembler. Assemblersprog er maskinorienterede sprog. Begrebsverdenen i disse sprog udgøres af computerens regneregistre, lagerceller og ydre enheder, og der er ingen hentydninger til den menneskeskabte opgave, programmet skal løse. Programmering i assembler er derfor et arbejde for specialister; man kan udnytte computeren yderst effektivt, men det er omstændeligt at skrive et fejlfrit assemblerprogram.

Imperative sprog

Fra slutningen af 1950'erne fremkom den klasse af programmeringssprog, der har fået den største udbredelse i praksis, de procedureorienterede sprog. De kaldes også imperative sprog, fordi de sætninger, et program består af, har karakter af kommandoer, der udføres i en bestemt, på forhånd fastlagt rækkefølge. Disse sprog gør det muligt at udtrykke en algoritme i en notation, der er naturlig i forhold til den opgave, der skal løses, og i mindre grad retter sig imod computerens egen interne struktur. Programmer i et imperativt sprog transformeres til maskinsprog vha. en oversætter. I disse sprog er computerens referencer til regneregistre og lagerceller skjult og erstattet af det abstrakte begreb en variabel. En variabel har et navn og en værdi. Navnet defineres af programmøren, og værdien fremkommer som resultat af de beregninger, programmet udfører. Navnet henviser egentlig til en celle i computerens lager, og denne celle indeholder værdien. Referencen er skjult for programmøren og styres af oversætteren og operativsystemet.

En vigtig nyskabelse var indførelsen af typer. Alle variable erklæres som havende en eller anden type, der afgrænser manipulationsmulighederne. I et assemblersprog repræsenteres der data uden hensyn til, hvad de betyder, hvilket giver mange fejlkilder. Man kan fx komme til at addere en dato og et efternavn ved blot at anføre en forkert lageradresse i en assemblerinstruktion. Indførelse af typer kan forhindre den slags fejl. Hvis to variable har typerne hhv. "tal" og "tegn", er det forbudt at addere dem, og en sådan fejl findes af oversætteren. Eksempler på typer for variable er heltal (erklæres fx som int eller integer), flydende tal (float eller real), den logiske type (ofte kaldet boolean efter matematikeren George Boole) og tegn (character).

Annonce

Procedurer. Et procedureorienteret programmeringssprog kan indeholde procedurer, som er programenheder, der udfører specifikke, afgrænsede opgaver. Man kommer ofte ud for, at den samme dataproces skal udføres mange gange og fra forskellige steder i et program, fx beregning af matematiske funktioner som logaritmer eller trigonometriske funktioner. Sådanne funktioner er ikke indbygget i computerens hardware, men må programmeres vha. addition, subtraktion, multiplikation og division. Det er derfor praktisk, at sproget tillader, at programmøren kun skriver selve koden (proceduren) én gang og derefter henviser til den, når der er behov for det.

Man kan opdele procedurer i to slags: indbyggede og programmørdefinerede. Indbyggede procedurer er dem, der er en del af sproget, og som programmøren kan aktivere blot ved at anføre et kald i sit program. En procedure, der defineres af programmøren, skal derimod være en del af programmet, men kan kaldes på samme måde som de indbyggede procedurer. Man kan også opdele procedurer i dem, der blot udfører et eller andet, og dem, der returnerer en værdi. De sidstnævnte kaldes funktionsprocedurer eller blot funktioner; de kan indgå som led i beregningsudtryk. En procedure, der sorterer et talsæt efter tallenes størrelse, er et eksempel på den første kategori, mens de matematiske procedurer som fx logaritmer er funktioner.

Et kald består af navnet på proceduren samt evt. en eller flere parametre. Kaldet af en procedure, der sorterer et talsæt, kan fx se således ud: sort(numbers,n), hvor sort er procedurens navn, og parametrene numbers, og n er navne på variable, der indeholder hhv. talsættet og antallet af tal. Denne procedure vil normalt ikke være indbygget, og sorteringsalgoritmen må derfor defineres af programmøren. Kald af funktioner kan eksemplificeres ved følgende sætning i sproget Pascal:
z := abs(y + sin(x))/2;
Dette er en tildelingssætning, der beregner værdien af den variable z som det halve af den numeriske værdi af summen af den variable y og sinus taget af den variable x. Både abs og sin er indbyggede funktioner i Pascal. Funktionskald har altså samme status i et udtryk som variable og konstanter.

Sætningstyper. Et imperativt programmeringssprog indeholder sætningstyper til konstruktion af de basale programstrukturer værditildeling, forgrening og gentagelse. Ved en forgrening undersøges først, om en betingelse er opfyldt, og der udvælges herved et programafsnit, der bliver udført, mens et andet måske springes over. Gentagelsessætninger fører til løkker, programstumper, der gennemløbes et bestemt antal gange eller et antal gange, der afhænger af beregningerne i løkken. I de fleste sprog findes der også mulighed for direkte ændring af den rækkefølge, sætningerne udføres i. Det sker ved at navngive de sætninger, man ønsker at "hoppe" til, og fra et andet sted i programmet kan man så udføre en såkaldt goto-sætning ('gå til'-), der refererer til navnet på en af de navngivne sætninger.

Sammensatte datatyper. I moderne sprog søger man at forøge abstraktionsniveauet på forskellig vis. Fx tillader typen array, at man grupperer data af samme type som en indiceret datamængde med et fælles navn. I mange sprog findes også mulighed for at gruppere data af forskellige typer i datastrukturer, der går under navne som structures (i C) eller records (i Pascal). I mange sprog er det endvidere muligt at definere nye typer til eget brug. Med indkapsling gøres et større eller mindre programforløb til en udelelig enhed med veldefinerede kontaktmuligheder til andre dele af programmet. En procedure er et eksempel på indkapsling, men indkapslingsprincippet kan føres langt videre. I de sprog, der bruges i dag, er det således almindeligt, at man kan definere programenheder, der indeholder såvel datastrukturer som de procedurer, der opererer på dem. Man kan på denne måde definere nye typer i sproget, kaldet abstrakte datatyper (ADT). En ADT har en veldefineret grænseflade til omverdenen, men implementeringen af funktionaliteten er skjult. Man kan derfor ændre de indre datastrukturer eller algoritmer, uden at det har virkning på det omgivende program; dette kaldes information hiding. Klasser (classes) i de objektorienterede sprog (se objektorienteret programmering) og pakker (packages) i Ada er eksempler på sådanne sprogkonstruktioner; de gør det muligt at opdele hele programmet i veldefinerede segmenter, der evt. kan genbruges i andre programmer eller danne basis for opbygning af nye segmenter.

Deklarative sprog

Et imperativt sprog afspejler den klassiske computers struktur med én CPU og ét lager. Moderne computere kan imidlertid have mange processorer, og det kan tænkes, at fremtidens computer vil have næsten lige så mange processorer som lagerceller. Det vil betyde, at lagerbegrebet ændres drastisk eller helt forsvinder, og i så fald svarer strukturen af programmeringssproget ikke til strukturen af computeren. Det kan skabe problemer, der bl.a. opstår, når flere processer opererer på fælles variable. Det klassiske eksempel er et pladsreservationssystem, hvor man må forhindre, at det samme flysæde reserveres samtidigt fra to forskellige billetsalgssteder. Sådanne situationer er vanskelige at tage højde for i imperative sprog.

Programmeringssprog, der ikke eksplicit specificerer nogen rækkefølge af beregningerne, men snarere er en statisk beskrivelse af, hvad der skal beregnes, kaldes for deklarative sprog. Sådanne sprog befinder sig på et højere abstraktionsniveau end de imperative sprog. De skjuler computerens tekniske konstruktion og er derfor bedre egnede til at løse problemer som dem, der er nævnt ovenfor.

Funktionalsprog. En deklarativ sprogform, som mange eksperter mener bliver fremtidens programmeringssprog, er funktionalsprogene. I disse sprog udføres alle beregninger vha. funktioner; der er ingen tildelingssætninger, kun parametre til funktioner, og funktionerne udveksler data med hinanden ved at være parametre for hinanden. Bortskaffelsen af tildelingssætningen har store konsekvenser. I et imperativt sprog kan man ikke forhindre såkaldte sideeffekter, dvs. at en procedure tildeler en værdi til en variabel, der ikke er parameter til proceduren (en global variabel); proceduren skal blot kende navnet på denne variable. Men det betyder, at rækkefølgen af kald kan blive afgørende for det endelige resultat af en beregning (to parallelle procedurer kan tænkes at ændre værdien af samme globale variabel). Hvis der ikke findes tildelingssætninger, vil funktioner altid opføre sig helt som matematiske funktioner, uden sideeffekter. Det betyder, at den præcise orden, i hvilken funktionerne kaldes, ikke har indvirkning på det endelige resultat af en beregning, og man kan derfor uden risiko for fejl udføre forskellige funktioner på samme tid i en computer med mange processorer. På denne måde løses samtidighedsproblemer i computere med mange processorer på en naturlig måde.

Dette afspejler en generel tendens ved design af programmeringssprog: Man forsøger at skabe sprog, der i kraft af selve deres struktur forhindrer eller vanskeliggør uhensigtsmæssig programmering (fx programmering med sideeffekter). LISP er et eksempel på et funktionalsprog, om end det ikke helt lever op til kravene for et rent funktionalsprog, idet der findes en form for tildelingssætning. Sproget Logo er også et funktionalsprog; det er bedst kendt for den såkaldte skildpaddegrafik, hvor de indbyggede funktioner kan bruges til på en skærm at tegne streger, der efterligner en skildpadde, der bevæger sig over skærmen. Fælles for alle funktionalsprog er, at de bruger lister som den fundamentale datastruktur og derfor indeholder et rigt udvalg af funktioner, der opererer på lister. Elementerne i en liste er atomer (svarende til simple variable) eller andre lister. Et eksempel på en liste med tre elementer er: [A,[B,C],35], hvor A er et atom, [B,C] er en liste med to atomer og 35 er et atom. Lister kan bruges til at opbygge store netværk af data, som man har behov for i bl.a. kunstig intelligens. Funktionalsprogene fordrer normalt ikke eksplicit angivelse af typer, men indeholder alligevel mulighed for at kontrollere, om man gør noget forkert (såkaldt svag typificering). Det betyder, at mange fejl, der i et imperativt sprog findes allerede under oversættelsen, i et funktionalsprog først findes, når programmet udføres.

Fjerdegenerationssprog

I udviklingen af programmeringssprog har man i tidens løb fjernet sig mere og mere fra computerens tekniske struktur og bevæget sig op på et højere og højere abstraktionsniveau. Førstegenerationssprogene var maskinsprog, det laveste abstraktionsniveau i praksis, som består i at bruge binære tal til repræsentation af selve elektronikken. Anden- og tredjegenerationssprog var assemblersprogene og de procedureorienterede sprog, hvor computerens struktur forsvinder mere og mere, men hvor det dog stadig er nødvendigt at specificere opgaveløsninger som detaljerede algoritmer. I fjerdegenerationssprogene, også kaldet 4GL (Fourth Generation Languages), er abstraktionsniveauet endnu højere, da det i disse sprog ikke er nødvendigt at specificere algoritmiske detaljer. En sætning som:
SELECT PRODUKTNAVN, ANTAL FROM VARELAGER WHERE ANTAL > 100
er en typisk repræsentant for en sætning i et 4GL, her i databasesproget SQL (Structured Query Language). SQL-systemet ved, hvordan man udtrækker PRODUKTNAVN og ANTAL fra filen VARELAGER, og forlanger derfor ikke, at programmøren specificerer de nødvendige algoritmer. Noget sådant kan kun lade sig gøre i et sprog, der er rettet imod et bestemt anvendelsesområde. Det er netop fordi, SQL handler om databaser (og ikke fx om lineær algebra), at systemet selv kan generere de algoritmiske detaljer.

Der findes ikke nogen skarp definition af, hvad et 4GL er, og betegnelsen bruges i flæng om enkeltprodukter til specielle formål og store programsystemer centreret om databaser og med mange tilhørende udviklingsværktøjer. Sprog inden for CAD/CAM må henregnes til 4GL, men 4GL finder også anvendelse inden for fx statistiske beregninger, computerstøttet undervisning, computerdesign, styring af robotter og ekspertsystemer. Det største anvendelsesområde for 4GL er dog administrativ databehandling. Sprogene er meget forskellige, men har den fordel frem for generelle sprog, at de bruger den terminologi, der findes inden for sprogets anvendelsesområde, og derfor bedre forstås af de mennesker, sproget henvender sig til. Sprogene er ofte udviklet af brugerne selv, og mange af dem bevæger sig på grænsen af, hvad man vil kalde et egentligt programmeringssprog; de er snarere meget fleksible programpakker, hvis funktion styres af inddata, der har karakter af kommandoer (et eksempel er SPSS, Statistical Package for the Social Sciences). Introduktionen af den personlige computer har øget behovet for programmeringssprog på meget højt niveau til brug for millioner af nye brugere, der ikke har egentlig programmeringsekspertise. Det har skabt produkter som regneark, databasesystemer o.l., der er så grafisk orienterede (vinduesbaserede) og menustyrede, at arbejdet med dem ikke længere ligner traditionel programmering.

Læs mere om programmer.

Referér til denne tekst ved at skrive:
Henning Bernhard Hansen: program (Programmeringssprog) i Den Store Danske, Gyldendal. Hentet 26. maj 2019 fra http://denstoredanske.dk/index.php?sideId=145599