Jens Elbæk
Hjem Ferie
Computer
Sprog
Musik
Geografi Vin Chaos (En) Game of Life (En) Sodoku (En)

Programmeringssprogenes generationer

Programmeringssprog har udviklet sig i de ca. 60 år, de har eksisteret.
Der er (for mig i det mindste) nogle ting som karakteriserer et programmeringssprog.

  • Det skal kunne foretage beregninger
  • Det skal kunne gentage sine instruktioner
  • Referere til memory, med symboler og navne. Kaldet variable eller feltnavne
  • Det skal (selvfølgelig) kunne udføres på en computer. Dette udelukker pseudo-programmeringssprog.

Udviklingen er sket i trin. Man regner normalt med tre, måske fire, generationer. Der har imidlertid være ret store forbedringer ind imellem, især i tredje generation.

Første generations sprog

I starten blev de skrevet i maskinkode. Det var, og er, næsten ulæseligt, da computeren (CPU'en) kan udføre dem direkte. Det kalder man Første Generations Programmeringssprog.

Anden generations sprog

Computerproducenterne udviklede ret hurtigt Assembler Language, som stadig havde en èn-til-èn sammenhæng med maskinkoden, men instruktioner og memoryadresser var læsbare og forståelige. Assembler Language er Anden Generations Programmeringssprog.

Tredje generations sprog

De to første generationer var ikke rigtig beregnet til at lave forretningsprogrammer og de var bundet til en bestemt computer (CPU). Det var uheldigt af flere årsager. Brugerne kunne ikke skifte computerleverandør og stadig beholde deres forretningsprogrammer og programmørerne kunne ikke nemt skifte mellem forskellige computere og operativsystemer. Det resulterede i Tredje Generation Programmeringssprog, som var rettet mod problemet, som de skulle løse, snarere end computeren de skulle køre på.

Her er nogle få eksempler:

  • COBOL
  • Fortran
  • C
  • Pascal
  • Java
  • C++
    Opfundet af danskeren Bjarne Stroustrup

Fjerde generations sprog

Man var nu oppe på 3 generationer og så er det selvfølgelig ikke svært at forestille sig, at "dygtige" sælgere vil sælge 4. generations-sprog. Meningen skulle være, at programmøren (næsten) kun skulle tænke på forretningslogikken og så skulle systemet lave teknikken. Så nem er verden desværre ikke og det bliver ikke bedre af, at nogle emsige forretningsfolk, arkitekter og programmører vil "tilpasse" det til netop deres firma.

Den har jeg også prøvet. En direktør var blevet dygtig besnakket, og brugte millioner på at indføre Fjerde generations sprog, - bare for at afskaffe det 10 år senere. Udviklingen gik i en anden retning, men jeg fik en del flyveture over Atlanten.

Hvad er ikke et programmeringssprog

Der findes sprog til computere, som ikke er programmeringssprog, hvor HTML er nok det mest kendte. Her er min, formentlig, ufuldendte liste:

HTML
Hyper Text Mark-up Language. Definerer formatet på en web-side.
CSS
Cascading Style Sheets. Definerer udseendet, sammen med HTML, af en Web-side.
SQL
Structured Query Language. Bruges til at hente og tilføje data fra en relationsdatabase.

Udviklingen af logikken i programmeringssprogene.

Tredje generations sprogene udviklede sig, dog uden at kunne kalde sig for en ny generation.

Ingen "Go To"

En computers fornemste opgave er at gentage instruktioner utallige gange meget hurtigt. Det bliver gjort ved at gå tilbage til en tidligere instruktion og starte forfra igen og igen, - en løkke (loop). At "gå tilbage" betød, at programmøren lavede en "Go To" tilbage. Det kunne betyde, at en programmør kunne lave "smarte" programmer, hvor hun lod programmet gå et helt andet sted i programmet. Det kunne blive noget forfærdeligt rod, så dygtige folk fandt i starten af 1970'erne på at forbyde brugen af "Go To". Nu måtte programmøren lave sin løkke med en instruktion, som f.eks. While eller For. Hun kunne nu ikke længere få programmet til at forsætte et helt andet sted end for enden af løkken.

Jeg var et sted, hvor man virkelig havde styr på, hvad programmørerne gjorde, så de fortsatte længe med "Go To", for programmørerne var disiplinerede. Programmører kan stadig lave rodede programmer, selvom de ikke må bruge "Go To", men der er dog lagt lidt bånd på dem. Gode programmer afhænger ikke af programmeringssprogene, men af programmørens intelligens.

Genbrug af instruktioner

Allerede fra starten af sprogenes udvikling, erkendte man, at nogle funktioner i et program kunne genbruges fra flere steder i programmet.

Procedurelle Programmer

Der var derfor smart at isolere disse instruktioner i sekmenter ("rutiner/subrutiner"), som kunne "kaldes" fra andre steder i programmet. Det kunne f.eks. være en subrutine til dato, validering af kundenummer osv. Disse selvstændige kodesekmenter, gav også et bedre overblik over et komplekst program. Denne type program blev kald et "procedurelt" program. Alle var glade, for nu skulle man kun kode et sekment kode èn gang. En rettelse i sekmentet/rutinen, slog igennem alle steder. Der blev lavet selvstændige programmer, som kunne kaldes fra mange forskellige programmer. Altså ingen funktion, rutine eller sekment skulle laves flere gange.

Object Orienteret Programmering

I slutningen af 1970'erne kom nogle meget smarte folk med et paradigmeskift inden for programmering. De havde opfundet en ny måde at genbruge kodesekmenter, funktioner og rutiner på. De kaldte det Object Orientert Programmering (OOP). Ikke nok med, at de havde fundet en ny måde at programmere på, men de brugte også en helt ny terminologi. Jeg kan ligeså godt indrømme det. Jeg var totalt forvirret!! Der var helt nye ord for de samme ting:

Class
Program
Method
Funktion/rutine
Object
Værdi eller flere værdier

Det allesværeste at forstå var det såkaldte "nedarvning". Jeg kan stadig ikke forstå, at man brugte dette ord, når "udvidelse" lå lige for. I programmerne skriver man "extends", - men det måtte det åbenbart ikke hedde.

Nedarvning (eller udvidelse) bestod af, at man lagde de mest generelle rutiner i en klasse/program for sig selv. Lad os forestille os forskellige klasser til kunder og leverandører. De har nogle fælles værdier og metoder (rutiner). Det kunne være "Adresse". Vi kunne derfor lave en klasse til "Person". Alle personer har jo en adresse. Du skal derfor "nedarve" din kundeklasse fra personklassen, - som også kaldes en "super"-klasse. Du skriver bare i starten af din kunde-klasse at det "extends Person". Vupti, - nu har din kunde-klasse adgang til alt i Person-klassen. Derved kan du umiddelbar bruge metoder og værdier fra Person-klassen. Det er faktisk smart, - men måske kunne de godt have arbejdet lidt med terminologien.

Objectorienteret programmering blev stort i firserne og et firma, der ellers kørte normale COBOL-programmer, mente at de kunne lave Objectorienterede COBOL-programmer. Nu var det desværre bare således at Cobol ikke i sig selv var lavet til objectorientering. Det afskrækkede imidlertid ikke firmaet. Det var direktøren, der havde fået denne fikse ide, - så pengekassen blev lukket op på vid gab.

Det gik galt, og firmaet var sikkert også kraftig til grin i visse kredse. Det endte med utallige bitte små programmer, som blev kaldt alle steder fra, og det gik selvfølgelig ud over performance, for slet ikke at tale om overblikket over programporteføljen.

Senere kom der faktisk objectorienteret COBOL, som kunne samarbejde med Java, men jeg ved ikke, hvor udbredt, det er blevet.

OO-eksempel i JavaScript


class Person
{
  constructor(fornavn, efternavn) 
  {
    this._fornavn = fornavn;
    this._efternavn = efternavn;
  }
  get fornavn()
  {
    return this._fornavn;
  }
  get efternavn()
  {
    return this._efternavn;
  }

  forNavn()
  {
    return this._fornavn;
  }
  efterNavn()
  {
    return this._efternavn;
  }

  get fuldeNavn()
  {
    return this.forNavn() + " " + this.efterNavn();
  }
}
class Kunde extends Person
{
    constructor(kundenummer, fornavn, efternavn)
    {
        super(fornavn, efternavn);
        this._kundenummer = kundenummer;
    }
    get kundenummer()
    {
        return this._kundenummer;
    }
    kundeNummer()
    {
        return this._kundenummer;
    }
    kundeNavn()
    {
        return this.kundenummer+ " " 
        + this.fornavn + " " 
        + this.efternavn;
    }
}

const kunde = new Kunde(10, "Jens", "Elbæk");
document.getElementById("demo").innerHTML = kunde.fuldeNavn;

Computeruafhængighed

Det har været et stort ønske at blive uafhængig af computerleverandørernes maskiner (platforme). COBOL var det første forsøg. Det betød, at man skulle have en "compiler" til at oversætte COBOL-instruktionerne til ens egen computers maskinkode. Det fungerede faktisk rimelig godt mellem de forskellige leverandører. Man siger, at programmerne er source-compatible. Leverandørerne kunne bare ikke nære sig fra at lave deres egne COBOL-instruktioner, som jo så ikke ville virke med andre compilere og dermed andre computere, - men generelt virkede det. Programmeringssproget C fungerede også fint mellem maskiner, - men sprogene kunne ikke altid fjerne sig fra hardwaren, de kørte på. De væsentligste er nok karakterset og intergers.

Det store paradigmeskift (og det mener jeg) blev fremkomsten af Java. Java var tænkt som den endelige løsning på platformsuafhængighed. Karaktersettet i Java er Unicode og Intergers er Big-endian. Compileren oversætter ikke til maskinkode, men til et sprog, som kaldes "byte-code". Det er syntakskorrekt, men ikke maskinkode. Java skal køre i sit eget operativsystem (OS), som kaldes "Java Virtual Machine" (JVM). JVM er sådan set "bare" en program på en computer, som simulerer et operativsystem til Java-programmer. Hvis et operativsystem vil køre Java, må det kunne køre JVM, som en slags OS. Alle computer- og OS-leverandører lavede deres egen JVM, så de også kunne køre Java. Det er smart! Det må da køre langsomt med alle de OS'er og udførelse af byte-code, - siger du. Ikke nødvendigvis. Det fungerer faktisk ret godt og er nu meget udbredt.

De store computerleverandører gik nu 100% ind for Java, - bl.a. IBM og Oracle, men selvfølgelig skulle Microsoft skille sig ud. De lavede deres egen "Java" og kaldte det C#, - udtales på engelsk "c sharp". Det så ens med Java at visse kodestumper kan compileres med en Java-compiler og som Java-programmør har man ingen problemer med at skifte.

Jeg havde engang et møde med en direktør for et IT-firma, som leverede løsninger til mindre firmaer. Han kom ind på om han skulle vælge Java eller C# som firmaets programmeringssprog. Jeg vurderede, at det ville tage for lang tid at forklare begge sprog for ham, så han selv kunne vælge. Han ville nok alligevel ikke forstå det, så jeg kom med en sammenligning:

Du ved formentlig godt, at Muslimernes Koran og Jødernes Gamle Testamente er næsten ens. De har de samme historier, bl.a om Abraham, som begge folkeslag mener, de nedstammer fra. Der er dog mindre forskelle, - bl.a. fordi der er føjet lidt til Koranen, som ikke er med i Det Gamle Testamente.
Det er det samme med Java og C#, - hvor C# kom efter Java. Forskellene er minimale og alligevel bliver der "udkæmpet religiøse krige om hvilket sprog, der er bedst".

Compilering eller fortolkning

Jeg har allerede talt lidt om compilering, hvor et programmeringssprog bliver tjekket for syntaksfejl og derefter oversat til maskinkode. En anden metode er at fortolke, dvs. læse programsourcen og udføre, hvad der står.

  • Cobol, C, Fortran osv. skal compileres til maskinkode.
  • Java og C# skal også compileres, men kun til et syntakskorrekt sprog, kaldet byte-code, som senere kan fortolkes.
  • JavaScript og Python skal slet ikke at compileres, men fortolkes umiddelbart inden de skal udføres. Det er lidt som Java's JVM, som fortolker java's byte-code. Det er selvfølgelig rart at slippe for compileringen, men så bliver syntaksfejl først fundet i testen af programmet. Syntaks- og logiske fejl bliver blandet sammen. Fordelene er dog så store, at det efterhånden er meget almindeligt. Det er bl.a. fordi at de fleste syntaksfejl bliver fundet af editorerne og at computerne er så hurtige, at oversættelsen inden udførelsen ikke betyder noget. Nogle har, til det formål, lavet en Just-In-Time compiler.

Editorer

Det måske største skridt i programudviklingen, er udviklingen af editorer. Man kan vel sige, at programeditorer, er en speciel teksbehandlingssystem til programmernes kildekode (source code). I starten var det kun indtasting på en skærme, men kort efter blev instruktionerne farvelagt og med PC'en fremkomst kom der virkelig gang i udviklingen. Editorerne kunne rette syntaksfejl og foreslå navne og instruktioner til programmøren.

Det var en fantastisk udvikling og især da programmøren kunne teste sine programmer direkte i kildekoden. Han kunne udføre en instruktion af gangen og inspicere værdierne i programmet, - måske endda ændre den. Det var nærmest et paradigmeskift.

Versionskontrol

Et program skal helst blive færdig på et tidspunkt og frigives til almindelig brug. Man siger det skal i produktion. Det er bare ikke så nemt, som det lyder. Programmet er testet i et testmiljø, hvor det har været isoleret fra andre systemer. Det vil nok være en god ide, at teste programmet op mod alle andre systemer på computeren. Derfor har man et miljø mellem test og produktion. Kært barn har mange navne. Det kan hedde: systemtest, ???.

Flytningen af programmerne mellem miljøerne skulle et helt system stå for: Change Control System (CCS).
Det burde da være nemt at lave, ikke sandt. Det er det ikke. CCS skal sikre, at programmer, der er afhængige af hinanden, bliver flyttet sammen og måske endnu vigtigere, skal det også fjernes igen sammen hvis der viser sig en fejl i miljøet. Man skal kunne se hvilke ændringer, der er foretaget i de forskellige versioner og dermed måske finde fejlen.

Når de nye programmer er testet mod alle andre programmer på computeren, skulle alt vel være i sin skønneste orden. Nej, vi har lige glemt at teste det sammen med brugerne. Vi lægger derfor endnu et testmiljø ind, - nu mellem sytemtest og produktion. Det kan kaldes pre-prod. Dataene kan være en kopi af produktionsdataene, - eller i hvert fald en delmængde af dem. Optimalt set bør det være "rigtige" brugere eller superbrugere, der tester. Når de godkender systemmet, kan det endelig sættes i produktion. Vi har altså nu fire miljøer. Mangler der nogle. Ja, administratorne af operativsystemer, kan ikke bare lægge deres nye OS i programmørernes testmiljø. Det kunne jo indeholde fejl. Vi lægger derfor endnu et testmiljø ind. Denne gang før testmiljøet. Vi har nu disse miljøer:

  • OS-test
  • Programtest
  • Systemtest
  • Pre-produktion
  • Produktion

Synes du, der ser lidt overvældende ud? Hvis der er noget en ledelse af en IT-afdeling hader, så er det, at der er fejl i et nyigangsat programkompleks. Specielt hvis det fejlende system har rettet data i databasen. Jeg vil ikke komme ind på hvad databaseadministratorerne har af arbejde sammen med programmørerne ifm et nyt system.

Jeg havde ansvaret for den tekniske del af et handelssystem på børsen. Selvom den "almindelige" produktion var vigtig, så var dette da specielt vigtigt. En politiker kaldte børshandlerne for "hysteriske kældinger". Det kan jeg skrive under på. Vi måtte ikke bare bruge en nyt OS til det handelssystem, med mindre det havde kørt fejlfrit i den "almindelige" produktion. Vi fik derfor en slags "post-produktion".