Gå til indholdet

Introduktion til programmering

Programmering er en metode til at give en computer beskeder og opgaver, så den ved præcist, hvad den skal udføre. Det er som at give en anden person en liste over opgaver med detaljerede instruktioner for hver enkelt opgave, så de kan udføre dem effektivt og præcist. Det er en kreativ proces, hvor du kan bringe dine idéer til live ved hjælp af logisk kode.

Det kan i høj grad være en udfordrende opgave at lære at beskrive opgaver og processer til en computer. Se denne video, hvor en far beder sine to børn om at fortælle ham, hvordan han, som var han en computer, der kun forstår simple operationer, skal lave en sandwich.

Det er en sjov måde at forklare, hvordan en computer fungerer, og hvor vigtigt det er at være præcis og detaljeret i dine instruktioner. Programmering handler om at tænke logisk og struktureret, så du kan beskrive opgaver på en måde, som en computer kan forstå og udføre.

Computere er enkle maskiner, der kan udføre simple opgaver hurtigt, men de kan kun gøre, hvad de bliver fortalt. Med programmering kan du få computeren til at udføre opgaver for dig ved at skrive kode, der beskriver, hvad du vil have computeren til at gøre, og hvordan den skal gøre det.

Koden kan være samlet i små programmer, der løser simple opgaver. For eksempel kan der på få linjer kode beregnes renter på lån eller tændes og slukkes en lampe baseret på information om solopgang og solnedgang. Men det kan også være millioner af linjer med instruktioner, der sammen løser komplekse opgaver, som f.eks. operativsystemer, skattesystemer med store mængder data og mange brugere, eller systemer bag kunstig intelligens.

Information til undervisere

Denne side bruger jeg og andre (husk at læse forsiden omkrng betingelser for brug) til at undervise/tale om programmering. Der er flere ting jeg synes der er vigtige i denne sektion:

  • De studerende skal forstå hvad en computer er for en størrelse - at den ikke gør noget med mindre den konkret bliver bedt om det
  • De bør have en helt basal forståelse for talsystemer - binær, hex og decimal. Igen - se https://mcronberg.github.io/csdemo/
  • De behøver ikke forstå NAND, AND, OR og andre gates men det vil være godt med en kort introduktion. Forståelsen af hvordan en computere kan lægge sammen (adder) og huske (latch). Jeg bruger mine egne eksempler på kredsløb på https://mcronberg.github.io/csdemo/ - men helt ærligt bliver det hurtigt unødig information for begyndere. Desværre bliver jeg selv lidt grebet af det hele og kommer nogen gange til at bruge for lang tid 😄
  • De bør prøve at skrive lidt kode i Notepad, kompilere og afvikle. Brug .NET “Hello World” eller lignende - måske Python. De behøver ikke forstå koden - det er processen der er vigtig
  • De skal forstå hvad en hukommelse er for en størrelse, og hvad en variabel er
  • De skal helt basalt kende til løkker, betingelser, funktioner og fejlhåndtering
  • De må meget gerne kende lidt historie om hardware og om personlighederne
    • Hvad med en tur på Teknisk Museum for at lure på DASK
  • Inden undervisning sørg for at:
  • Visual Studio Code er installeret
    • Måske Github Copilot
  • .NET er installeret
  • Python er installeret
  • Git er installeret (og brugernavn og email er sat op)
    • git config –global user.name “Dit Brugernavn”
    • git config –global user.email “dinemail@eksempel.com
  • GitHub konto er oprettet og klar
  • ChatGpt er logget ind
  • ZoomIt er installeret

Hvorfor lære programmering?

Softwareudvikling åbner mange muligheder. Uanset om du vil bygge hjemmesider, mobilapps, spil, virksomhedsoftware eller automatisere processer, kan det give mange fordele. Det handler om mere end at skrive kode; det kræver problemløsning, kreativitet, samarbejde og kontinuerlig læring. Selv hvis du ikke arbejder som udvikler, kan de tilegnede færdigheder være værdifulde i andre felter.

Softwareudvikling er en kreativ proces, der involverer at løse problemer og bygge løsninger. Det kræver kreativ tænkning og innovative løsninger. Udviklere er efterspurgte, og behovet stiger. Det giver mange jobmuligheder og mulighed for en stabil og velbetalt karriere. Uanset om du er interesseret i webudvikling, appudvikling, spiludvikling eller noget helt fjerde, er der mange muligheder for at finde et job, der passer til dine interesser og færdigheder.

Softwareudvikling opfordrer til teamwork, kommunikation og videndeling. Det er et felt, hvor man lærer fra andre og opbygger netværk. Der er altid noget nyt at lære, og det kræver, at man holder sig opdateret med de nyeste teknologier. Det er en dynamisk karriere, der tilbyder personlig og professionel vækst.

En anden fordel ved softwareudvikling er, at det kan være en fantastisk hobby. Ligesom at bygge med LEGO, kan du skabe noget unikt og spændende ud fra dine ideer. Det giver en følelse af tilfredsstillelse at se et projekt komme til live, og det kan være en sjov og udfordrende måde at tilbringe din fritid på. Uanset om du udvikler små spil, personlige værktøjer eller eksperimenterer med nye teknologier, kan softwareudvikling være en yderst givende hobby.

Derudover udvikler du værdifulde færdigheder som logisk tænkning og systematisk problemløsning, som er anvendelige i mange andre områder. At lære programmering kan også give dig en bedre forståelse af den teknologi, der omgiver os i hverdagen, og åbne døre til mange spændende muligheder i fremtiden.

Hvorfor lære programmering i en tid med kunstig intelligens?

Selvom kunstig intelligens (AI) og avancerede sprogmodeller som ChatGPT bliver mere udbredte og kraftfulde, er der stadig mange gode grunde til at lære programmering:

  • Forståelse af teknologi: Ved at lære programmering får du en dybere forståelse af, hvordan teknologier som AI fungerer, hvilket gør dig bedre rustet til at anvende og udnytte dem effektivt.

  • Kontrol og tilpasning: AI-værktøjer kan være meget kraftfulde, men de er ikke altid perfekte. Ved at kunne programmere kan du tilpasse og forbedre AI-værktøjerne til dine specifikke behov og sikre, at de fungerer, som du ønsker.

  • Problemløsning: Programmering udvikler dine evner inden for logisk tænkning og systematisk problemløsning, færdigheder der er værdifulde i næsten alle aspekter af livet og arbejde.

  • Jobmuligheder: Selvom AI kan automatisere mange opgaver, er der stadig stor efterspørgsel efter dygtige programmører til at udvikle, vedligeholde og forbedre software og AI-systemer.

  • Kreativitet: Programmering giver dig mulighed for at bringe dine egne idéer til live og skabe unikke løsninger, spil, applikationer og meget mere. Det er en kreativ proces, der ikke kan erstattes fuldstændigt af AI.

  • Personlig udvikling: At lære programmering fremmer en livslang læringstilgang, hvor du konstant udvikler dine færdigheder og tilpasser dig nye teknologier og metoder.

  • Sikkerhed og etik: Med en forståelse af programmering kan du bedre vurdere sikkerheds- og etiske aspekter ved brug af teknologi og AI, og sikre at systemer udvikles og anvendes ansvarligt.

Selvom AI og sprogmodeller fortsætter med at udvikle sig, giver programmering dig de nødvendige værktøjer og færdigheder til at forblive relevant og konkurrencedygtig i en teknologidrevet verden.

Talsystemer

Hvis du er begynder indenfor softwareudvikling, er det vigtigt at have en grundlæggende forståelse af talsystemer - i hvert fald de tre mest almindelige talsystemer: decimal, binær og hexadecimal. Du vil konsekvent støde på disse talsystemer, når du arbejder med software, og det er vigtigt at forstå, hvordan de fungerer og hvordan de konverteres mellem hinanden. Det vil hjælpe dig med at forstå, hvordan computere repræsenterer og behandler data, og hvordan du kan arbejde med tal i din kode.

Så selvom det virker som en tør og teknisk detalje, er det et vigtigt emne at have dækket, inden vi begynder at snakke om programmering.

Det decimale talsystem

Det decimaltalsystem er det talsystem, vi bruger i vores dagligdag. Det er baseret på ti cifre: 0, 1, 2, 3, 4, 5, 6, 7, 8 og 9. Hvert ciffer repræsenterer en potens af 10, hvor positionen af cifferet bestemmer værdien. For eksempel repræsenterer tallet 123 følgende potenser af 10:

  • Første position fra højre (3) er \(3 \cdot 10^0\), som er 3.
  • Anden position (2) er \(2 \cdot 10^1\), som er 20.
  • Tredje position (1) er \(1 \cdot 10^2\), som er 100.

For at finde den decimal værdi, lægger vi disse potenser sammen: \(100 + 20 + 3 = 123\). Dette er en simpel måde at forstå, hvordan decimaltalsystemet fungerer, og hvordan vi repræsenterer tal i vores dagligdag.

Skal man foretage simple beregninger - eksempelvis addition - er det er nemt at bruge det decimale talsystem:

  10
 + 8
 ---
  18

eller

   1  (menter)  
  123
 + 49
 ----
  172

Beregninger som disse kommer naturligvis ikke som nogen overraskelse, men det er vigtigt at forstå, at samme beregningsprincipper gælder for andre talsystemer.

Det binære talsystem

I det binære talsystem bruger vi kun to cifre, 0 og 1 og arbejder i potens af 2, i modsætning til det decimaltalsystem vi normalt anvender, som har ti cifre (0-9) og arbejder i potens af 10. Dette talsystem er fundamentet for al moderne computer teknologi, fordi det er ekstremt effektivt til elektronisk databehandling.

Hvorfor netop binært? Det handler om de mest grundlæggende elektroniske komponenter i en computer – transistorerne. En transistor i en computer kan have to tilstande: tændt eller slukket. Denne enkle, binære tilstand gør det muligt at repræsentere data med kun to cifre, hvor 0 typisk står for ‘slukket’ og 1 for ‘tændt’.

Hver binær cifre kaldes en ‘bit’, som er en sammentrækning af “binary digit”. Bits er byggestenene i det binære talsystem. Når vi kombinerer bits, kan vi danne mere komplekse datastrukturer. For eksempel bruger vi 8 bits til at danne en ‘byte’, og med forskellige kombinationer af otte tændte eller slukkede bits kan vi repræsentere alt fra tal til bogstaver og andre symboler.

Mest kendte forkortelser inden for det binære talsystem

Her er nogle af de mest kendte forkortelser og enheder, du vil støde på, når du arbejder med binære systemer i softwareudvikling:

  • Bit (b): Den mindste enhed af data i computeren. En bit kan være enten 0 eller 1.
  • Nibble: En halv byte, bestående af 4 bits.
  • Byte (B): En samling af 8 bits. En byte kan repræsentere 256 forskellige værdier (fra 0 til 255).
  • Word: En samling af bits, typisk 16, 32 eller 64 bits afhængig af computerarkitekturen.
  • Kilobit (Kb): 1.000 bits (ikke at forveksle med kibibit, som er 1.024 bits).
  • Kilobyte (KB): 1.000 bytes (ikke at forveksle med kibibyte, som er 1.024 bytes).
  • Megabit (Mb): 1.000.000 bits (ikke at forveksle med mebibit, som er 1.048.576 bits).
  • Megabyte (MB): 1.000.000 bytes (ikke at forveksle med mebibyte, som er 1.048.576 bytes).
  • Gigabit (Gb): 1.000.000.000 bits (ikke at forveksle med gibibit, som er 1.073.741.824 bits).
  • Gigabyte (GB): 1.000.000.000 bytes (ikke at forveksle med gibibyte, som er 1.073.741.824 bytes).
  • Terabit (Tb): 1.000.000.000.000 bits (ikke at forveksle med tebibit, som er 1.099.511.627.776 bits).
  • Terabyte (TB): 1.000.000.000.000 bytes (ikke at forveksle med tebibyte, som er 1.099.511.627.776 bytes).

Disse enheder og forkortelser er grundlæggende for forståelsen af, hvordan data måles og håndteres i computere.

For at forstå hvordan vi regner i binært, kan vi tage et simpelt eksempel. Betragt følgende binære tal: 00001011. Dette tal læses fra højre mod venstre, hvor hver position repræsenterer en stigende potens af 2:

  • Første position fra højre (1) er \(2^0\), som er \(1\).
  • Anden position (1) er \(2^1\), som er \(2\).
  • Tredje position (0) er \(2^2\), som er \(4\).
  • Fjerde position (1) er \(2^3\), som er \(8\).
  • Femte position (0) er \(2^4\), som er \(16\).
  • Sjette position (0) er \(2^5\), som er \(32\).
  • Syvende position (0) er \(2^6\), som er \(64\).
  • Ottende position (0) er \(2^7\), som er \(128\).

For at finde den decimal værdi, lægger vi kun de værdier sammen, hvor der er en 1’er i det binære tal: \(8 + 0 + 2 + 1 = 11\).

Så det binære tal 1011 svarer til det decimal tal 11:

| 128 | 64  | 32  | 16  |  8  |  4  |  2  |  1  |
|-----|-----|-----|-----|-----|-----|-----|-----|
|  0  |  0  |  0  |  0  |  1  |  0  |  1  |  1  |

= \(0 + 0 + 0 + 0 + 8 + 2 + 1 = 11\)

Det binære tal 10101010 svarer til det decimal tal 170:

| 128 | 64  | 32  | 16  |  8  |  4  |  2  |  1  |
|-----|-----|-----|-----|-----|-----|-----|-----|
|  1  |  0  |  1  |  0  |  1  |  0  |  1  |  0  |

= \(128 + 0 + 32 + 0 + 8 + 0 + 2 + 0 = 170\)

Dette system gør det muligt for computere at behandle og lagre en enorm mængde information meget effektivt og er grunden til, at binære talsystem er så fundamentalt i alt fra software programmering til databehandling og digital kommunikation.

Tip

Du kan eksperimentere med binære tal og konverteringer på https://mcronberg.github.io/csdemo/numbers.html. Prøv at konvertere mellem decimal og binær og se, hvordan tallene repræsenteres i de to talsystemer.

Skal man foretage simple beregninger - eksempelvis addition - fungerer det som i det decimale talsystem:

  00001010 (10)
  00000100 (8)
  --------
  00001110 (18)

eller

        1 (menter)
  00000001 (1)
  00000011 (3)
  --------
  00000100 (4)

Er det vigtigt for begyndere?

For de fleste begyndere i softwareudvikling er det ikke nødvendigt at have en dyb forståelse af det binære talsystem. Dog er det nyttigt at have en grundlæggende forståelse af, hvordan binære tal fungerer, da det er grundlaget for alt, hvad der sker i en computer. At forstå, hvordan data repræsenteres og behandles i binært, kan hjælpe dig med at forstå, hvordan computere fungerer og hvordan software fungerer på et grundlæggende niveau.

Kan du se hvad klokken er?

På diverse uddannelser er det normale ur over døren skiftet ud med et binært ur. Det er en sjov måde at lære at læse binære tal på. Her er et eksempel på hvordan et binært ur kan se ud:

Repræsentation af værdier i computere

Computere repræsenterer altså data i binær form ved hjælp af bits og bytes, og der er mange forskellige værdier du kunne ønske at gemme. Her er en grundlæggende gennemgang af, hvordan forskellige typer værdier repræsenteres:

Heltal

Heltal kan repræsenteres ved forskellige antal bytes:

  • 1 byte (8 bits): Kan repræsentere værdier fra 0 til 255 (uden fortegn) eller fra -128 til 127 (med fortegn).
  • 2 bytes (16 bits): Kan repræsentere værdier fra 0 til 65.535 (uden fortegn) eller fra -32.768 til 32.767 (med fortegn).
  • 4 bytes (32 bits): Kan repræsentere værdier fra 0 til 4.294.967.295 (uden fortegn) eller fra -2.147.483.648 til 2.147.483.647 (med fortegn).

Tip

Prøv at se https://mcronberg.github.io/csdemo/ram.html for at se hvordan simpel hukommelse i en computer er organiseret.

Negative tal

Negative tal repræsenteres typisk ved hjælp af den såkaldte to-komplement metode. For at repræsentere et negativt tal, inverteres (ændres) alle bits i tallet fra 0 til 1 og fra 1 til 0, og derefter lægges 1 til resultatet.

Eksempel: For et 4-bit system:

  • Tallet +3 repræsenteres som 0011.
  • For at finde repræsentationen af -3:
  • Inverter alle bits i 0011: 1100.
  • Læg 1 til resultatet: 1100 + 1 = 1101.

Så i et 4-bit system er +3 repræsenteret som 0011, og -3 er repræsenteret som 1101.

For at konvertere tilbage fra to-komplement til et negativt tal: - Hvis det første (venstre) bit er 1, er tallet negativt. - Inverter alle bits og læg 1 til resultatet.

Eksempel: For at konvertere 1101 tilbage til -3:

  • Inverter 1101: 0010.
  • Læg 1 til resultatet: 0010 + 1 = 0011.
  • Resultatet er -3.

Du behøver ikke at forstå detaljerne i to-komplement metoden - det er mere vigtigt at forstå, at computere bruger denne metode til at repræsentere negative tal.

Kommatal

Kommatal (decimaltal) repræsenteres ved hjælp af IEEE 754 standarden (også kaldet floating point), som deler tallet i tre dele: fortegn, eksponent og mantisse. De to mest almindelige repræsentationer er:

  • 32-bit (single precision): Bruger 1 bit til fortegn, 8 bits til eksponent, og 23 bits til mantisse.
  • 64-bit (double precision): Bruger 1 bit til fortegn, 11 bits til eksponent, og 52 bits til mantisse.

Således vil man i et 32-bit system kunne repræsentere tal som 3.14159 eller 1.2345678e-10, mens man i et 64-bit system kan repræsentere langt større og mere præcise tal.

Igen, det er ikke nødvigt at forstå detaljerne i IEEE 754 standard - det er vigtigere at forstå, at computere bruger denne metode til at repræsentere kommatal. Men der skal ikke meget fantasi til at forestille sig, at det kan være en udfordring at arbejde med kommatal i et binært system, og at der kan opstå afrundingsfejl hvis man ikke er opmærksom. Det kan resultere i fejl i beregninger, som kan have alvorlige konsekvenser i nogle situationer.

I videoen forklares det, hvordan en lille afrundingsfejl i Patriot missilsystemet under Golfkrigen i 1991 førte til, at systemet fejlede i at opspore og afværge en irakisk Scud-missil, hvilket resulterede i 28 døde og 260 sårede soldater. Fejlen opstod ved konvertering af tidspunkter til binære tal, hvilket resulterede i en positionsfejl på 573 meter.

Bogstaver

Bogstaver og andre tegn repræsenteres ved hjælp af tegnsæt som ASCII eller Unicode:

  • ASCII: Bruger 7 bits til at repræsentere 128 forskellige tegn. For eksempel er ‘A’ repræsenteret som 65.
  • Unicode: Bruger flere bytes til at repræsentere et stort antal tegn fra forskellige skriftsystemer.

For eksempel repræsenterer Unicode-tegnet ‘A’ stadig 65, mens det japanske tegn ‘あ’ er repræsenteret som U+3042.

Så når du skriver et Word dokument, sender en e-mail eller besøger en hjemmeside, bruger computeren disse tegnsæt til at vise bogstaver, tal og symboler på skærmen. Men alt er stadig baseret på binære tal, som computeren kan forstå og behandle. Så den binære repræsentation af “Hello, World!” kan se sådan ud:

01001000 01100101 01101100 01101100 01101111 00101100 00100000 01010111 01101111 01110010 01101100 01100100 00100001

fordi hvert bogstav er repræsenteret ved hjælp af en binær værdi.

H = 01001000
e = 01100101
l = 01101100
l = 01101100
o = 01101111
osv...
Du kan selv finde en ASCII tabel på nettet og prøve at konvertere “Hello, World!” til binære tal.
Summering

Forståelsen af disse grundlæggende repræsentationer er vigtig for at kunne arbejde med data i programmering, da det giver indsigt i, hvordan computere gemmer og behandler information.

Info

Kan du se hvordan alt i en klassisk computer er baseret på binære tal? Det er fundamentet for alt, hvad der sker i en computer, og det er derfor vigtigt at have en grundlæggende forståelse af, hvordan binære tal fungerer.

Du behøver ikke forstå 2-komplement metoden, IEEE 754 standard eller Unicode-tegnsæt - det er mere vigtigt at forstå, at computere bruger disse metoder til at repræsentere data, og at det er grundlaget for alt, hvad der sker i en computer.

Det hexadecimale talsystem

Det hexadecimale talsystem er et positionssystem med basis 16, hvilket betyder, at det bruger 16 forskellige cifre. Disse er de velkendte cifre fra 0 til 9 suppleret med bogstaverne A til F, hvor A repræsenterer 10, B er 11, C er 12, D er 13, E er 14, og F er 15 i det decimale system.

For at forstå hvordan man arbejder med hexadecimale tal, kan vi tage et simpelt eksempel. Betragt følgende hexadecimale tal: 11. Dette tal læses fra højre mod venstre, hvor hver position repræsenterer en stigende potens af 16:

  • Første position fra højre (1) er \(1 \cdot 16^0\), som er \(1\).
  • Anden position (1) er \(1 \cdot 16^1\), som er \(16\).

For at finde den decimale værdi, lægger vi værdierne sammen: \(1 + 16 = 17\).

Eller hvad med tallet 2E?

  • Første position fra højre (E) er \(14 \cdot 16^0\), som er \(14\).
  • Anden position (2) er \(2 \cdot 16^1\), som er \(32\).

For at finde den decimale værdi, lægger vi værdierne sammen: \(32 + 14 = 46\).

Hvad så med tallet FF?

  • Første position fra højre (F) er \(15 \cdot 16^0\), som er \(15\).
  • Anden position (F) er \(15 \cdot 16^1\), som er \(240\).

For at finde den decimale værdi, lægger vi værdierne sammen: \(240 + 15 = 255\).

Slutteligt - hvad med tallet ABCD?

  • Første position fra højre (D) er \(13 \cdot 16^0\), som er \(13\).
  • Anden position (C) er \(12 \cdot 16^1\), som er \(192\).
  • Tredje position (B) er \(11 \cdot 16^2\), som er \(2816\).
  • Fjerde position (A) er \(10 \cdot 16^3\), som er \(40960\).

For at finde den decimale værdi, lægger vi værdierne sammen: \(40960 + 2816 + 192 + 13 = 43981\).

Kan du se hvad klokken er?

Tidligere nævnte jeg, at det klassike ur over døren i undervisningslokalerne var skiftet ud med et binært ur. Der findes også hexadecimale ure, og her er et eksempel på hvordan et hexadecimal ur kan se ud:

Det hexadecimale system bruges ofte i programmering og informationsteknologi, fordi det er mere kompakt end det binære system, men stadig kan mappes direkte til det, hvilket gør det enklere at læse og skrive binære data. Hver hexadecimal ciffer kan præcist repræsentere fire binære cifre (bits), hvilket gør oversættelsen mellem de to systemer meget effektiv. For eksempel svarer et enkelt hexadecimal ciffer ‘F’ direkte til de binære cifre ‘1111’, og hexadecimal ‘2’ svarer til ‘0010’.

Denne direkte korrespondance gør det muligt at reducere kompleksiteten og længden af binære tal, hvilket er særligt nyttigt i felter som digital elektronik og computerprogrammering, hvor man ofte skal håndtere lange sekvenser af bits. At arbejde med hexadecimale tal reducerer risikoen for fejl, da det er lettere at skrive og kontrollere færre cifre.

For at konvertere fra binær til hexadecimal, grupperer man de binære cifre i grupper af fire fra højre mod venstre, og omdanner hver gruppe til det tilsvarende hexadecimale tal. Omvendt kan man let omdanne et hexadecimalt tal til binær ved at erstatte hvert hexadecimale ciffer med den tilsvarende gruppe af fire binære cifre.

Her er et par eksempler:

  • 0011 svarer til 3 i decimal og 3 i hexadecimal.
  • 1001 svarer til 9 i decimal og 9 i hexadecimal.
  • 1010 svarer til 10 i decimal og A i hexadecimal.
  • 1011 svarer til 11 i decimal og B i hexadecimal.

Ved større binære værdier kan det være nyttigt at opdele dem i grupper af fire for at lette konverteringen til hexadecimal.

For eksempel vil det binære tal 1101110110₂ konverteres til hexadecimal således:

  1. Opdel det binære tal i grupper af fire fra højre: 1101 1101 10
  2. Tilføj eventuelt foranstillede nuller for at fuldføre den sidste gruppe: 0011 0111 0110
  3. Omdan hver gruppe til det tilsvarende hexadecimale tal: 3 7 6

Derfor er 1101110110 binært lig med 376 i hexadecimalt. Denne teknik anvendes bredt i programmering, især til adressering af hukommelse og ved farvekodning i digitale medier, hvor hexadecimal kode er standard.

Det oktale talsystem

Udover det decimale, binære og hexadecimale talsystem, findes også det oktale talsystem, som er baseret på otte cifre (0-7). Dette talsystem bruges sjældent i moderne computere. Det oktale talsystem er dog stadig relevant i nogle sammenhænge, især i gamle systemer og i nogle former for digital kommunikation.

Hvorfor kan man købe en t-shirt med 2A på?

Tidligere så vi, hvordan “Hello, World!” kunne repræsenteres i binære tal. Hvad med den hexadecimale repræsentation af “Hello, World!” - kunne der være en årsag til at man kan købe en t-shirt med “48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21” på brystet?

48x = 01001000b = H
65x = 01100101b = e
6Cx = 01101100b = l
osv...

Måske har du læst “Hitchhiker’s Guide to the Galaxy” af Douglas Adams, hvor svaret på alt er 42. I ASCII er 42 repræsenteret som ‘*’, så måske er svaret på alt et stjernetegn - der som bekendt i mange operativsystemer repræsenterer “alt”? Kan det forklare en t-shirt med “42” i binær form (00101010) eller hexadecimale form (2A)? Er 2A virkelig svaret på alt? 😄

Lidt om hardware

For bedre at forstå softwareudvikling er det vigtigt at have en grundlæggende forståelse af, hvordan computere fungerer, herunder både traditionelle og kvantecomputere. Traditionelle computere bruger binær logik og transistorer til at behandle information. Kvantecomputere derimod opererer på principperne om kvantemekanik. De bruger qubits, som kan repræsentere flere tilstande samtidig, hvilket gør dem meget mere kraftfulde i visse scenarier. For nuværende skal du blot huske: traditionelle computere bruger binær logik og transistorer, mens kvantecomputere bruger qubits og kvanteporte.

En kort historie om computing

“Computing” begyndte med simple mekaniske enheder til aritmetiske operationer. Disse udviklede sig til sofistikerede elektroniske systemer. Tidlige computere, som ENIAC og UNIVAC, var massive maskiner, der brugte relæer og radiorør. Opfindelsen af transistoren i slutningen af 1940’erne revolutionerede computing, hvilket muliggjorde mindre, hurtigere og mere pålidelige computere. Senere gjorde integrerede kredsløb og mikrochips computere endnu mindre og førte os ind i den digitale tidsalder.

Mens der er meget mere at fortælle om computerens historie, skal du for nuværende blot sætte pris på rejsen fra tidlige mekaniske enheder til nutidens kraftfulde computere.

Note

At udforske computerens historie kan være fascinerende og hjælper dig med at værdsætte moderne teknologi. Mange museer viser udviklingen af computing, som:

Der er også film og dokumentarer, der dækker computerens historie, såsom “The Imitation Game” (2014), “The Social Network” (2010), “Pirates of Silicon Valley” (1999), “Triumph of the Nerds” (1996) og “The Machine That Changed the World” (1992). Se i det mindste “The Imitation Game”—det er en fantastisk film om Alan Turing, Enigma-maskinen og kodebryderne i Bletchley Park under Anden Verdenskrig. Det er også en hyldest til en af de store hjerner i computerhistorien, og historien om hans liv er både fascinerende og tragisk.

Transistorer

I hjertet af enhver moderne elektronisk enhed er transistoren. En transistor er en lille elektronisk switch, der kan tænde eller slukke en strøm, hvilket repræsenterer de binære tilstande 0 og 1 (tændt eller slukket, sandt eller falsk). Disse binære tilstande er grundlaget for al digital computing. Transistorer er pakket i integrerede kredsløb eller mikrochips, der udfører de logiske operationer og beregninger, der er nødvendige for at køre software. I dag kan milliarder af transistorer placeres på en enkelt mikrochip, hvilket muliggør de kraftfulde enheder, vi bruger hver dag.

Transitor

Info

Du har måske hørt om Moores lov, som siger, at antallet af transistorer på en mikrochip fordobles cirka hvert andet år. Denne observation, foretaget af Gordon Moore i 1965, har holdt stik i flere årtier og har drevet den hurtige udvikling af computerteknologi.

Moore’s lov (fra Wikipedia)

Traditionel computer

Traditionelle computere, ofte kaldet klassiske computere, er afhængige af binær logik til at behandle information. Transistorer og logiske porte er byggestenene i disse computere, hvilket giver dem mulighed for at udføre beregninger, lagre data og køre programmer. Som begynder behøver du ikke at vide meget om logiske porte, men det er godt at have en grundlæggende forståelse af, hvordan de fungerer.

Porte og logik

En logisk port er en grundlæggende byggesten i digitale kredsløb, der udfører en grundlæggende logisk operation. Der er flere typer logiske porte, herunder AND-, OR- og NOT-porte, hver med deres egen funktion. Tænk på en port som en meget lille elektronisk komponent, der tager en eller flere indgange og producerer en udgang baseret på indgangene. Input og output er binære, hvilket betyder, at de kan være enten 0 eller 1.

Logiske porte/gates
NOT-port

En NOT-port er den simpleste komponent. Den tager én input (A) og outputter den modsatte værdi (Q). Hvis A er 0, er Q 1, og omvendt. Dette kan skrives som Q = NOT A, og det kaldes også en inverter. Du vil se dette meget i programmering, især når du arbejder med binære værdier. NOT-porten kan vises i en sandhedstabel:

A Q
0 1
1 0

Læs tabellen sådan: Hvis A er 0, er Q 1. Hvis A er 1, er Q 0.

AND-port

En AND-port tager to eller flere inputs og producerer en output på 1, kun hvis alle inputs er 1. Hvis nogen input er 0, vil output være 0. Dette kan skrives som Q = A AND B. Sandhedstabellen for en to-input AND-port ser sådan ud:

A B Q
0 0 0
0 1 0
1 0 0
1 1 1

Læs tabellen sådan: Q er 1, kun når både A og B er 1.

OR-port

En OR-port tager to eller flere inputs og producerer en output på 1, hvis mindst én af inputs er 1. Output er 0, kun hvis alle inputs er 0. Dette kan skrives som Q = A OR B. Sandhedstabellen for en to-input OR-port er:

A B Q
0 0 0
0 1 1
1 0 1
1 1 1

Læs tabellen sådan: Q er 1, hvis enten A eller B er 1, eller hvis begge er 1.

XOR-port

En XOR (exclusive OR) port tager to inputs og producerer en output på 1, hvis præcis én af inputs er 1. Hvis begge inputs er ens, er output 0. Dette kan skrives som Q = A XOR B. Sandhedstabellen for en to-input XOR-port er:

A B Q
0 0 0
0 1 1
1 0 1
1 1 0

Læs tabellen sådan: Q er 1, kun når A og B er forskellige.

Tip

Du kan eksperimentere med logiske porte og sandhedstabeller på https://mcronberg.github.io/csdemo/gates.html. Prøv at ændre input og se, hvordan output ændres baseret på logikken for hver port.

NAND-, NOR- og NXOR-porte

NAND (NOT AND), NOR (NOT OR) og NXOR (NOT XOR) porte er kombinationer af de grundlæggende porte, vi har diskuteret. De kaldes universelle porte, fordi de kan bruges til at skabe enhver anden type logisk port. For eksempel kan en AND-port skabes ved at kombinere en NAND-port med en NOT-port. Disse porte er essentielle i digital kredsløbsdesign og er grundlaget for moderne computing.

Selvom du er nybegynder, kan forståelsen af disse grundlæggende begreber hjælpe dig med at sætte pris på, hvordan computere behandler information og udfører programmer. Mens du ikke behøver at være ekspert i digital logik, kan det at have en grundlæggende forståelse af logiske porte være gavnligt, når du dykker dybere ned i softwareudvikling. Det er ligesom at kende alfabetet, før du lærer at læse og skrive.

Info

Alle programmeringssprog bruger binær logik og udtryk, så forståelse af hvordan binær logik fungerer, kan hjælpe dig med at skrive bedre kode og forstå, hvordan computere behandler information. Du vil skrive mange AND- og OR-operationer i kode, og du vil ofte se binære operationer i kode. For eksempel udfører bitwise AND-operatoren (&) i C, C++, Java og andre sprog en logisk AND-operation på hvert par af tilsvarende bits. Forståelse af binær logik kan hjælpe dig med at skrive mere effektiv kode og forstå, hvordan computere behandler information.

Kombinationer af logiske porte

Kombinationen af logiske porte og transistorer danner grundlaget for digital computing. Ved at kombinere disse byggesten kan du opbygge mere komplekse kredsløb, der udfører avancerede operationer og beregninger.

Her er et eksempel på en fuldadder, der bruger AND-, OR- og XOR-porte til at udføre en binær addition af to bits. Denne kreds tager to bits (A og B) som input og producerer to output (Sum og Carry). Sum er resultatet af additionen, mens Carry er en overførsel fra en tidligere beregning. Denne kreds er en grundlæggende byggesten i digitale computere og bruges til at udføre binære additioner.

Eksempel på beregning med porte (Full adder)

Prøv den selv på https://mcronberg.github.io/csdemo/fulladder.html.

Samme logiske porte bruges også til at gemme information. Når en transistor er tændt (1), kan den repræsentere et stykke data. Ved at organisere millioner af transistorer i et komplekst system af gates kan vi gemme og hente store mængder data.

Her er et eksempel på en latch, der bruger porte til at gemme en bit af data. Latchen kan gemme en bit, indtil den nulstilles. Dette er en simpel form for hukommelse, der bruges i computere til at gemme midlertidige data.

Hukommelse / Latch

Prøv den selv på https://mcronberg.github.io/csdemo/srandorlatch.html.

CPU

Alle disse logiske porte og transistorer samles i computerens centralenhed (CPU). CPU’en er computerens hjerne, der er ansvarlig for at udføre instruktioner, udføre beregninger og håndtere data. Den består af flere komponenter, herunder den aritmetiske logiske enhed (ALU), kontrolenheden og registre. CPU’en henter instruktioner fra hukommelsen, dekoder dem og udfører dem for at udføre de opgaver, som softwaren kræver. Dens kompleksitet er forbløffende, men helt grundlæggende er CPU’en en samling af logiske porte og transistorer, der arbejder sammen for at behandle information.

Alle de forskellige funktioner i CPU’en, såsom at udføre beregninger, håndtere hukommelse og udføre instruktioner, kan udføres af et antal instruktioner, som CPU’en kan forstå. Disse instruktioner er skrevet i maskinkode, som er et lavniveau programmeringssprog, der direkte svarer til de binære instruktioner, som CPU’en kan udføre. Du kan se det som en binær kode (nuller og ettaller), der fortæller CPU’en, hvad den skal gøre, og nullerne og ettallerne er direkte relateret til strømmen i de elektroniske kredsløb. Basalt er det meget logisk og simpelt, men det kan være meget komplekst, når du ser på det store billede, og når du tilføjer hastighed til ligningen, øges kompleksiteten.

Alle CPU’er har en clock frekvens, som er antallet af cyklusser pr. sekund, som CPU’en kan udføre. Hver gang “uret” tikker, kan CPU’en udføre en instruktion (det er lidt mere komplekst end det - men du forstår pointen). Jo hurtigere klokfrekvensen er, jo flere instruktioner kan CPU’en udføre på en given tid. Dette er grunden til, at CPU’er med højere klokfrekvens generelt er hurtigere og mere kraftfulde end dem med lavere klokfrekvens. Hastigheden måles i Hertz (Hz), og 1 Hz er én cyklus pr. sekund. Moderne CPU’er har klokfrekvenser i gigahertz (GHz) området, hvilket betyder, at de kan udføre milliarder af instruktioner pr. sekund.

Hukommelse

Hukommelse er en anden væsentlig komponent i en computer. Den gemmer data og instruktioner, som CPU’en har brug for til at køre programmer. Der er forskellige typer hukommelse i en computer, såsom RAM (random access memory) og ROM (read-only memory). RAM er flygtig hukommelse, der gemmer data midlertidigt, mens computeren kører. Det er hurtigt, men mister sit indhold, når computeren slukkes. ROM, derimod, er ikke-flygtig hukommelse, der gemmer vigtige instruktioner til opstart af computeren. Det er langsommere end RAM, men beholder sit indhold, selv når computeren er slukket.

Fra et elektronisk perspektiv er hukommelse (igen) lidt som en stor matrix af elektroniske komponenter baseret på transistorer, der kan tændes eller slukkes. Hver transistor kan gemme en bit (0 eller 1), og ved at kombinere mange komponenter kan du gemme en masse data. CPU’en kan læse og skrive til hukommelsen, og hukommelsen kan tilgås tilfældigt (deraf navnet random access memory). Hukommelsen er forbundet med CPU’en med en bus, (lidt som er et sæt ledninger), der kan overføre data mellem CPU’en og hukommelsen. Databussen er som en motorvej for data, og jo bredere bussen er, jo mere data kan overføres på én gang. Derfor vil du ofte se computere beskrevet som 8-bit, 16-bit, 32-bit eller 64-bit computere, hvilket refererer til bredden af databussen.

Som nybegynder i softwareudvikling behøver du ikke at kende alle detaljerne om, hvordan hukommelsen fungerer, men forståelsen af, at midlertidige data gemmes i hukommelsen, mens computeren kører, kan hjælpe dig med at forstå begreber som variable, arrays, objekter og pointers i programmering. Hukommelsesstyring er en væsentlig del af softwareudvikling, og at vide, hvordan hukommelsen fungerer på et højt niveau, kan være gavnligt, når du skriver kode og bygger software.

Lager

Udover hukommelse har computere lagringsenheder som harddiske, solid-state drives (SSD’er) og flashdrev. Disse enheder gemmer data permanent, selv når computeren er slukket. Lagringsenheder er langsommere end hukommelse, men har meget større kapacitet. De bruges til at gemme operativsystemer, applikationer, filer og andre data, der skal bevares over tid. Når du gemmer en fil på din computer, gemmes den på en lagringsenhed, ikke i hukommelsen.

Lagringsenheder er som store biblioteker, hvor du kan gemme data i lang tid. Dataene gemmes på magnetiske diske eller flashhukommelseschips, og computeren kan læse og skrive data til og fra lagringsenhederne. Lagringsenhederne er forbundet med computeren med en bus, ligesom hukommelsen, men lagringsenhederne er langsommere end hukommelsen. Dette er grunden til, at du ikke kan køre programmer direkte fra en lagringsenhed; du skal først indlæse programmet i hukommelsen. Lagringsenhederne bevarer deres data, selv når computeren er slukket - i modsætning til RAM.

I/O-enheder

Input/output (I/O) enheder er periferiudstyr, der giver dig mulighed for at interagere med computeren. Disse enheder inkluderer tastaturer, mus, skærme, printere, scannere og netværksadaptere. I/O-enheder gør det muligt for dig at indtaste data i computeren og modtage output fra den. De er forbundet til computeren gennem porte, såsom USB, HDMI, Ethernet og andre. I/O-enheder er essentielle for at interagere med software og hardware, og de spiller en afgørende rolle i, hvordan du bruger en computer.

Bundkort

Bundkortet er computerens hovedkredsplade. Det forbinder alle computerens komponenter, såsom CPU, hukommelse, lagringsenheder og I/O-enheder. Bundkortet leverer de elektriske forbindelser og veje, der tillader data at flyde mellem komponenterne. Det er som computerens centralnervesystem, der koordinerer alle aktiviteterne og sikrer, at alt fungerer sammen uden problemer.

Tip

Hvis du er nysgerrig efter at vide, hvordan computere fungerer på et dybere niveau, kan du overveje at udforske området computerarkitektur. Det dykker ned i design og organisering af computersystemer, herunder CPU, hukommelse, lagring og I/O-enheder. Forståelse af computerarkitektur kan give dig en dybere forståelse for computerens indre funktioner og hvordan software interagerer med hardware.

Der er selvfølgelig mange ressourcer på internettet, der kan hjælpe dig med at forstå computerarkitektur, og du kan finde mange bøger om emnet. For begyndere, der bare vil have en grundlæggende forståelse af, hvordan computere fungerer, vil jeg anbefale at læse “But How Do It Know? - The Basic Principles of Computers for Everyone” af J. Clark Scott. Det er en god bog, der forklarer, hvordan computere fungerer fra bunden uden at blive for teknisk. En anden måde at få et indblik i, hvordan computere fungerer, er at bygge din egen computer. Det er ikke så svært, som det lyder, og der er mange vejledninger på internettet, der kan hjælpe dig med at bygge din egen computer fra bunden. Jeg vil anbefale at se Ben Eater’s YouTube-serie om at bygge en 8-bit computer fra bunden. Det er en fantastisk måde at lære om computerarkitektur og hvordan computere fungerer.

Hvis du er klar til opgaven, kan du endda købe en gammel CPU som 6502 eller Z80 fra 1980’erne (ja - de produceres stadig) og bygge din egen computer omkring den. Det er et projekt til 10-20 dollar og meget sjovt.

En meget simpel CPU

Alle computere har basalt kun nogle få opgaver de kan udføre.

  • Opbevare og hente data fra hukommelsen
  • Flytte data fra et sted til et andet
  • Udføre beregninger
  • Kommunikere med omverdenen (tastatur, skærm, netværk, osv.)

Alle disse operationer udføres typisk af en CPU (Central Processing Unit), og består i virkeligheden af en meget stor samling af transistorer and andre elektroniske komponenter samlet i logiske gates. Komponenterne er samlet i forskellige overordnede blokke som kan udføre forskellige opgaver:

  • Registre - opbevarer data
  • Databus - forbindelse til andre enheder
  • Clock - flytter data fra et sted til et andet ved at åbn og lukke for forbindelser i en rasende fart
  • Control Unit - styrer de andre komponenter
  • Arithmetic Logic Unit (ALU) - udfører beregninger
  • RAM - hukommelse til midlertidig opbevaring af data
  • ROM - hukommelse til permanent opbevaring af data
  • I/O - kommunikation med omverdenen

Alle disse komponenter kan styres af kontrollinjer som kan tændes og slukkes - og den helt simple forklaring på hvad programmering er, er at tænde og slukke for disse kontrollinjer. Det sker med binære instruktioner som CPU’en kan forstå, og der er ikke noget i vejen for at du selv kan skrive disse instruktioner ved at kigge på CPU’ens instruktionsmanual. Dette form for programmering kaldes for maskinkode, og er meget svært at lære og arbejde med fordi det er rene binære værdier og kræver en dyb forståelse for hvordan en CPU er opbygget.

Heldigvis findes der mange andre måder at skrive programmer på, og det er disse vi skal kigge på senere.

Det ligger langt uden for dette kapitel at forklare hvordan en CPU rent faktisk udfører opgaver, men hvis du har lyst kan du læse mere om det i min SAP CPU (simple as possible CPU). Der er også en del referencer på siden - herunder til Ben Eater, som har en samling forrygende videoer der viser hvor en CPU kan opbygges på breadboards.

3-tabel i SAP CPU (16 Hz)

Kvantecomputing

Kvantecomputing er et fascinerende og hurtigt udviklende felt, der lover at revolutionere computing. I modsætning til klassiske computere, der bruger binær logik og transistorer, opererer kvantecomputere på principperne om kvantemekanik (kvantefysik).

Designet af klassiske computere er baseret på transistorer og logiske porte til at behandle binær information. I sammenligning bruger kvantecomputere kvanteporte til at manipulere såkaldte qubits. Kvanteporte er mere komplekse og afhænger af kvantefænomener som superposition (superposition tillader qubits at være i flere tilstande på samme tid, hvilket betyder, at en qubit kan være både 0 og 1 samtidigt) og sammenfiltring (engelsk: entanglement, hvor to eller flere qubits bliver så tæt forbundne, at tilstanden af en qubit øjeblikkeligt kan påvirke tilstanden af en anden, uanset afstanden mellem dem). Denne fundamentale forskel gør det muligt for kvantecomputere at behandle flere muligheder på én gang, hvilket potentielt gør dem meget hurtigere til at løse visse komplekse problemer.

Hardware i kvantecomputere er også meget anderledes end i klassiske computere. Mens klassiske computere bruger siliciumbaserede chips med transistorer, bruger kvantecomputere fysiske elementer som atomer, ioner, fotoner eller superledende kredsløb til at skabe og manipulere qubits. Mange kvantecomputere bruger superledende kredsløb nedkølet til nær absolut nul, hvilket hjælper med at bevare kvantetilstande i længere perioder. Andre bruger fangede ioner i elektromagnetiske felter, manipuleret af lasere til at skabe og kontrollere qubits. Desuden bruger nogle kvantecomputere lyspartikler, eller fotoner, til at repræsentere qubits, der manipuleres med optiske enheder som strålesplittere og faseskiftere.

Programmering af kvantecomputere adskiller sig også markant fra programmering af klassiske computere. Klassisk programmering bruger traditionelle sprog som C, Python og Java, hvor programmer skrives som sekvenser af instruktioner, der manipulerer bits. Kvanteprogrammering bruger derimod specialiserede sprog som Qiskit, Cirq, Q#, og Quantum Assembly Language (QASM). Disse sprog giver programmører mulighed for at skrive kvantealgoritmer, der udnytter kvantemekanikkens egenskaber.

For eksempel, for at løse en labyrint ville en klassisk computer tjekke hver sti én efter én. En kvantecomputer derimod kan udforske flere stier samtidig og potentielt finde løsningen meget hurtigere. Denne parallelle behandlingskapacitet er en af de centrale fordele ved kvantecomputing.

Historisk set har mange store videnskabsmænd som Niels Bohr og Albert Einstein bidraget til udviklingen af kvantefysik. Bohr udviklede Bohr-modellen af atomet, mens Einstein, kendt for sin modstand mod nogle aspekter af kvantemekanikken, alligevel bidrog væsentligt med sin forklaring af den fotoelektriske effekt og teorier om kvanteentanglement.

Sammenfattende tilbyder kvantecomputere en ny måde at behandle information på, der udnytter de unikke egenskaber ved kvantemekanik. Mens klassiske computere er yderst effektive til mange opgaver, har kvantecomputere potentialet til at tackle problemer, der i øjeblikket ligger uden for rækkevidde for klassisk computing.

Tip

Hvis du er interesseret i kvantecomputing, er der mange ressourcer til at lære mere om emnet. IBM, Google, Microsoft og Rigetti er nogle af de førende virksomheder inden for kvantecomputing, og de tilbyder online ressourcer, kurser og værktøjer til at udforske kvantecomputing. Der er også mange bøger og artikler om emnet, såsom “Quantum Computing for Everyone” af Chris Bernhardt. At lære om kvantecomputing kan være en spændende rejse ind i en ny verden af computing og teknologi.

Programmering

Programmering handler om at løse problemer med hjælp af logik (betingelser og løkker), matematik (addere, substrahere, multiplicere mv) samt at opbevare værdier midlertidligt (variabler). På denne måde kan du løse en lang række opgaver, der ellers ville være tidskrævende eller måske endda praktisk umulige at løse manuelt.

Maskinkode

Med programmering skriver du instruktioner til en CPU, og i sin grundform er det en samling af binære værdier, som CPU’en kan forstå som et såkaldt instruktionssæt. Disse instruktiopner fortæller CPU’en, hvad den skal gøre, og hvordan den skal gøre det, og i virkeligheden er instruktionerne meget simple - som f.eks. at flytte en værdi fra et sted til et andet, at sammenligne to værdier, at udføre en beregning eller at springe til en anden del af programmet. Instruktionerne er helt basalt besked om at tænde og slukke for kontrollinjer i CPU’en ved hjælp af spænding.

Hver CPU har sit eget instruktionssæt, som definerer de operationer, den kan udføre, og hvordan den udfører dem. Instruktionssættet er unikt for hver CPU og er designet til at udnytte CPU’ens specifikke egenskaber og funktioner.

Som et eksempel kan den gamle 6502 CPU (Apple II, Commodore 64 mv) gemme en værdi i et lille register i CPU’en med en enkelt instruktion 10101001, som fortæller hvilke kontrollinjer der skal tændes og slukkes for at gemme en værdi i registeret. Instruktionen kombineres med en værdi, der skal gemmes - eksempelvis 00001010 for at gemme værdien 10 (decimalt). Den samlede kommado vil så være 10101001 00001010 - eller skrevet i hexadecimale værdier A9 0A. Næste instruktion kunne være at flytte værdien fra register A til register X. Den kommando hedder 10001010 eller 8A i hexadecimale værdier. Næste kommenda kunne være at lægge 1 til værdien i register X. Den kommando hedder 11101000 eller E8 i hexadecimale værdier. Så med tre kommandoer

10101001 00001010
10001010
11101000

har vi gemt værdien 10 i register A, flyttet værdien fra register A til register X og lagt 1 til værdien i register X så den nu indeholder 00001011 (11 decimalt).

Det er lidt nemmere at skrive i hexadecimale værdier:

A9 0A
8A
E8

men i sidste ende er det blot en samling af binære værdier, som CPU’en kan forstå.

Denne form for programmering kaldes for maskinkode (enkelt operationer for opcode), og det er meget komplekst og kræver en dyb forståelse for, hvordan en CPU er opbygget. Det er også meget tidskrævende og fejlfølsomt, da en enkelt fejl kan få hele programmet til at bryde sammen.

Men alle programmer vil kunne skrives i rå maskinkode. Det vil tage en krig af tid og vil være meget svært at læse - men det er muligt.

Maskinsprog (assembly)

For at gøre det lidt lettere at skrive programmer i maskinkode, er der udviklet et sprog kaldet assembly, som er et lavniveau (tæt på hardware) programmeringssprog, der er tæt knyttet til CPU’ens instruktionssæt. Assembly giver programmører mulighed for at skrive instruktioner, der svarer til CPU’ens binære instruktioner. Hver assembly instruktion svarer direkte til en binær instruktion, og det gør det lettere at skrive og læse programmer sammenlignet med ren maskinkode.

Førnævte 6502 CPU program kunne skrives i assembly, og det kunne se sådan ud:

LDA #$0A
TAX
INX

LDA betyder “load accumulator”, som er et register i CPU’en. #$0A betyder at værdien 10 skal gemmes i registeret. TAX betyder “transfer accumulator to X register”, og INX betyder “increment X register by one”. Så disse tre instruktioner gør det samme som de tre maskinkode instruktioner ovenfor.

Det er meget nemmere for os mennesker at forstå og skrive, men CPU’en forstår jo naturligvis intet af disse instruktioner - de skal oversættes til binære instruktioner, som CPU’en kan forstå. Dette gøres med et program kaldet en assembler, som oversætter assembly koden til binære instruktioner.

I sin helt grundlæggende form er en assembler blot et program som, ved hjælp af en tabel, oversætter assembly instruktioner til binære instruktioner, som CPU’en kan udføre. I mere moderne assembly sprog er der også mulighed for at bruge labels, som er symboler der repræsenterer en hukommelsesadresse, samt makroer, som er en måde at genbruge kode på. Dette gør det lettere at skrive og vedligeholde assembly programmer.

Men selvom assembly er lettere at skrive og læse end maskinkode, er det stadig meget tæt på hardwaren og kræver en dyb forståelse for CPU’ens instruktionssæt og funktioner. Assembly programmering er ofte brugt i systemnære programmer, drivere og andre applikationer, der kræver direkte kontrol over hardwaren, og mange programmeringssprog oversætter “blot” deres kode til assembly, som så igen oversættes til binære instruktioner.

Du vil opdage, at programmering i mange henseender er abstraktion på abstraktion på abstraktion på … Allerede nu ved du, at maskinekode er for svært at arbejde med, og at assembly er en smule lettere. Senere vil du lære om højniveau programmeringssprog, som er endnu lettere at arbejde med. Men abstaktion er ikke blot en fordel - jo længere du kommer væk fra hardwaren, jo mindre kontrol har du over, hvad der sker, og jo mindre effektivt vil dit program køre. Så det er en balancegang mellem kontrol og effektivitet på den ene side og kompleksitet og abstraktion på den anden.

Info

Blot for at få nogle begreber på plads: Det at programmere kaldes også at udvikle eller skrive kode, instruktioner kaldes også for kodelinjer, og en samling af instruktioner kaldes for kildekode.

Højniveau programmeringssprog

For at gøre det endnu lettere at skrive programmer, er der udviklet højniveau programmeringssprog, som er designet til at være mere læselige og forståelige for mennesker. Disse sprog bruger mere naturlige og abstrakte begreber, som gør det lettere at skrive og læse kode. Højniveau programmeringssprog er mere abstrakte end assembly og maskinkode, og de giver programmører mulighed for at fokusere på problemløsning og logik i stedet for at bekymre sig om de tekniske detaljer i CPU’en.

Simpelt eksempel på kode i flere sprog

Her er et eksempel kode som tæller fra 1 til 9, og udskriver om tallet er mindre end 5 eller større eller lig med 5 - altså:

1 is less than 5
2 is less than 5
3 is less than 5
4 is less than 5
5 is greater or equal to 5
6 is greater or equal to 5
7 is greater or equal to 5
8 is greater or equal to 5
9 is greater or equal to 5

Koden viser nogle af de begreber du finder i alle programmeringssprog – variabler, løkker, betingelse og muligheden for at kommunikere med brugeren. Mere om disse begreber senere – her er først koden skrevet visuelt :

Brug af Google Blocky (her MIT App Inventor)

Maskinkode

Her er koden skrevet i rå maskinkode for en x86 (ish) CPU - det er meget svært at læse og forstå:

1011 1110 0100 0000 0000 0000 1010 1100 0011 1100 0000 0000 0111 0100 0000 0101 1010 0010 0100 0100 0000 0000 0010 1100 0011 0000 0000 0100 0011 000 1010 0010 0100 0100 0000 0000 0011 1100 0011 0101 0111 0010 0000 0101 1110 1011 1110 0111 1000 0000 0011 1110 0000 0000 0000 0000 0111 0100 0000 0101 1110 1011 1110 0111 1011 1000 0000 0001 0000 0000 0011 0001 1101 1011 1100 1101 1000 0000 

Det gør det ikke meget lettere at læse som hexadecimale værdier:

BE 40 00 AC 3C 00 74 05 A2 44 00 2C 30 04 30 A2 44 00 3C 35 72 05 EB E7 80 3E 00 00 00 00 74 05 EB E7 B8 01 00 31 DB CD 80

Ingen ville skrive kode på denne måde men det er det eneste en CPU forstår!

Assembly

Lidt lettere at læse og forstå er koden skrevet i assembly (her i syntaxen der forstås af https://www.onecompiler.com):

section .data
    less_than_five db ' is less than 5', 10, 0
    greater_equal_five db ' is greater or equal to 5', 10, 0
    numbers db '1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6', 0, '7', 0, '8', 0, '9', 0

section .bss
    number resb 4

section .text
    global _start

_start:
    mov esi, numbers

loop_start:
    lodsb                     ; Load byte at address (ESI) into AL and increment ESI
    cmp al, 0                 ; Compare AL with 0 (end of string)
    je check_next             ; If AL is 0, check the next number

    mov [number], al          ; Store AL into number
    mov byte [number + 1], 0  ; Null terminate for print_number

    sub al, '0'               ; Convert ASCII to integer
    add al, '0'               ; Convert back to ASCII for printing
    mov [number], al          ; Store back the ASCII character in number

    cmp al, '5'               ; Compare with ASCII '5'
    jb print_less_than_five   ; Jump if below 5

print_greater_equal_five:
    mov ecx, number
    call print_number

    mov edx, 26               ; Length of " is greater or equal to 5" + newline
    mov ecx, greater_equal_five
    mov ebx, 1
    mov eax, 4
    int 80h
    jmp loop_start

print_less_than_five:
    mov ecx, number
    call print_number

    mov edx, 16               ; Length of " is less than 5" + newline
    mov ecx, less_than_five
    mov ebx, 1
    mov eax, 4
    int 80h
    jmp loop_start

check_next:
    cmp byte [esi], 0         ; Check if next byte is also 0 (end of array)
    je end_program            ; If yes, end the program
    jmp loop_start            ; Otherwise, continue the loop

print_number:
    mov edx, 1
    mov ebx, 1
    mov eax, 4
    int 80h
    ret

end_program:
    mov eax, 1
    xor ebx, ebx
    int 80h

Det er stadig meget tæt på hardwaren, men det er meget lettere at læse og forstå end maskinkode. Assembly programmering er stadig meget kompleks og kræver en dyb forståelse for CPU’ens instruktionssæt og funktion.

Prøv at se om det virker på https://www.onecompiler.com/assembly.

Basic

Så vi vil langt hellere skrive koden i et højniveau programmeringssprog som f.eks. BASIC, Python eller C#.

Her er samme kode skrevet i et af de helt gamle programmeringssprog kaldet BASIC:

DIM i as Integer
FOR i = 1 TO 9
IF i < 5 THEN
PRINT i & " is less than 5"
ELSE
PRINT i & " is greater or equal to 5"
END IF
NEXT i

Prøv at se om det virker på https://www.onecompiler.com/basic.

Python

Her - samme kode i et ældre men stadig meget populært sprog kaldet Python:

for i in range(1, 10):
    if i < 5:
        print(str(i) + " is less than 5")
    else:
        print(str(i) + " is greater or equal to 5")

Prøv at se om det virker på https://www.onecompiler.com/python.

C

Her er koden i programmeringssproget C:

#include <stdio.h>

int main() {
    int i;
    for (i = 1; i < 10; i++) {
        if (i < 5) {
            printf("%d is less than 5\n", i);
        } else {
            printf("%d is greater or equal to 5\n", i);
        }
    }
    return 0;
}

Se om det virker på https://www.onecompiler.com/c.

Java

public class Main {
    public static void main(String[] args) {
        for (int i = 1; i < 10; i++) {
            if (i < 5) {
                System.out.println(i + " is less than 5");
            } else {
                System.out.println(i + " is greater or equal to 5");
            }
        }
    }
}

Se om det virker på https://www.onecompiler.com/java.

C#

using System;

class Program {
    static void Main() {
        for (int i = 1; i < 10; i++) {
            if (i < 5) {
                Console.WriteLine(i + " is less than 5");
            } else {
                Console.WriteLine(i + " is greater or equal to 5");
            }
        }
    }
}

Se om det virker på https://www.onecompiler.com/csharp.

JavaScript

for (let i = 1; i < 10; i++) {
    if (i < 5) {
        console.log(i + " is less than 5");
    } else {
        console.log(i + " is greater or equal to 5");
    }
}

Se om det virker på https://www.onecompiler.com/javascript.

Haskell

main :: IO ()
main = do
    mapM_ checkNumber [1..9]

checkNumber :: Int -> IO ()
checkNumber i =
    if i < 5
    then putStrLn (show i ++ " is less than 5")
    else putStrLn (show i ++ " is greater or equal to 5")

Se om det virker på https://www.onecompiler.com/haskell.

Ruby

(1..9).each do |i|
    if i < 5
        puts "#{i} is less than 5"
    else
        puts "#{i} is greater or equal to 5"
    end
end

Se om det virker på https://www.onecompiler.com/ruby.

Kompilering

Oversættelsen fra kildekoden til assembler og fra assembler til binære instruktioner kaldes kompilering, og denne proces er ofte automatiseret af et værktøj kaldet en kompiler. Hvis du installerer et programmeringssprog som Python, C# eller Java er det i virkeligheden denne kompiler, samt en del andre værktøjer du installerer.

I princippet kunne du altså selv skrive de rå binære instruktioner hvis du kender instruktionsmanualen til den pågældende CPU. Noget nemmere ville det være for dig at skrive assembler som så oversættes til binære instruktioner af en kompiler, og langt nemmere vil det være at skrive C#, Python eller andre sprog. Denne forskel i abstraktionsniveau er årsagen til, at de moderne sprog kaldes højniveau sprog, hvor mellemkode og assembler kaldes lavniveau sprog.

Liste over forskellige programmeringssprog

Liste over forskellige programmeringssprog

Der findes en masse programmeringssprog og der udvikles hele tiden nye. Her er en liste over nogle af de mest kendte. Bemærk i øvrigt hvor mange danskere (med fed) der har bidraget til udvikling af forskellige sprog:

  • COBOL fra 1960’erne
    • Howard Bromberg og flere
  • BASIC
    • John George Kemeny og Thomas Eugene Kurtz på Dartmouth College (USA) i starten af 1960’erne
  • C
    • Dennis Ritchie (Bell Labs) i starten af 1970’erne
  • C++
    • Bjarne Stroustrup (Bell Labs) i starten af 1980’erne
  • Objective-C
    • Tom Love og Brad Cox er også fra 1980’erne
  • Java
    • James Gosling fra Sun Microsystems (nu Oracle) fra 90’erne
  • JavaScript (som ikke må forveksles med Java)
    • Brendan Eich (Netscape - senere Mozilla/Firefox) i 1990’erne
  • C#
    • Anders Hejlsberg hos Microsoft (2002)
  • PHP
    • Rasmus Lerdorf omkring 1995
  • Delphi
    • Anders Hejslberg hos Borland i 90’erne
  • Ruby
    • Yukihiro Matsumoto i 1995
  • Python
    • Guido van Rossum i 1991
  • Dart
    • Lars Bak og Kasper Lund

Se StackOverflow’s survey - her fra 2021 men find selv sidste version.

Tip

Der findes et super spændende værktøj kaldet OneCompiler som kan oversætte kildekode fra mange forskellige sprog og se resultatet - uden at skulle downloade en compiler. Der er også mange opgaver og eksempler på kode.

Igen et par begreber at slå fast: Kildekode er instruktioner skrevet i en speciel syntaks som sprogets kompiler forstår og kan oversætte. Denne proces kaldes kompilering. Tit bliver det også omtalt som at bygge. Kildekode oversat til binære instruktioner som kan afvikles af et operativsystem kaldes et program, applikation eller blot en app.

Udviklingsmiljøer

Alle former for programmering ender i sidste ende i tekstfiler (py-filer for Python, cpp-filer for C++, cs-filer for C#, js-filer for JavaScript mv), og i princippet kunne man sagtens bruge simple teksteditorer som Notepad, VIM og andre lignende. Men en stor del af det at lære at programmere handler også om at benytte det rigtige udviklingsværktøj.

Når du programmerer, kan det være en fordel at bruge et dedikeret udviklingsmiljø, også kaldet en IDE (Integrated Development Environment). En IDE tilbyder en række funktioner, der kan gøre kodningen lettere og mere effektiv. For eksempel inkluderer en IDE typisk funktioner som syntaksfremhævning, auto-fuldførelse af kode, debugging-værktøjer og versionstyring.

  • Syntaksfremhævning hjælper med at gøre koden mere læsbar ved at farvekode forskellige elementer som nøgleord, variabler og kommentarer. Dette kan gøre det lettere at spotte fejl og forstå koden hurtigt.
  • Auto-fuldførelse af kode foreslår automatisk kode, mens du skriver, hvilket kan spare tid og mindske risikoen for stavefejl. For eksempel, hvis du skriver en variabels navn, vil IDE’en foreslå fuldførelsen af navnet, så du ikke behøver at skrive det hele.
  • Debugging-værktøjer gør det muligt at finde og rette fejl i koden mere effektivt. Du kan sætte breakpoints, som stopper eksekveringen af koden på bestemte steder, og derefter inspicere værdierne af variablerne for at forstå, hvad der går galt.
  • Mange IDE’er har indbygget understøttelse af versionsstyring, såsom Git. Dette gør det muligt at holde styr på ændringer i koden, samarbejde med andre udviklere og rulle tilbage til tidligere versioner af koden, hvis der opstår problemer.

Eksempler på IDE’er

Der findes mange forskellige IDE’er, hver med sine egne fordele og ulemper. Her er nogle populære eksempler:

  • Visual Studio: En kraftfuld IDE fra Microsoft, der understøtter mange forskellige programmeringssprog, herunder C#, og har mange avancerede funktioner.
  • JetBrains Rider: En IDE specielt designet til .NET-udvikling, som tilbyder hurtig ydeevne og mange produktivitetsværktøjer.
  • Visual Studio Code: En letvægts, men alligevel kraftfuld editor, der kan tilpasses med udvidelser til at understøtte mange forskellige programmeringssprog og funktioner.
  • Eclipse: En populær IDE, især kendt for Java-udvikling, men den understøtter også mange andre sprog gennem plugins. Eclipse er open source og har et stort økosystem af udvidelser, der gør det muligt at tilpasse og udvide funktionaliteten efter behov.
  • IntelliJ IDEA: En IDE fra JetBrains, der er meget brugt til Java-udvikling, men også understøtter en bred vifte af andre sprog og teknologier. IntelliJ IDEA er kendt for sin smarte kode-assistance, fejlkorrektion og dybdegående integration med forskellige byggesystemer og versionstyringsværktøjer.
  • PyCharm: En IDE fra JetBrains, der er specialiseret til Python-udvikling. PyCharm tilbyder avanceret syntaksfremhævning, debugging, integration med populære web-frameworks som Django og Flask, samt support for data science-værktøjer som Jupyter notebooks.

Hver af disse IDE’er har sine egne unikke funktioner og fordele, der kan gøre dem velegnede til specifikke typer af projekter og udviklingsmiljøer.

Prøv det selv - Hello World

Den bedste måde at lære at programmere på er at prøve det selv – skrive noget kode, få det kompileret og efterfølgende afviklet. Du kan finde en guide her. Du kan nu prøve at åbne filen Program.cs som er din kildekode. Den ser nogenlunde således ud:

// måske en kommentar
Console.WriteLine("Hello, World!");

Sørg for at koden kan kompilere og afvikles (se førnævnte guide), og prøv så at tilrette koden, gemme og kompilere og afvikle igen. Se om du kan retten teksten til at skrive noget andet. Prøv også at lave en fejl ved eksempelvis at slette et semikolon, gemme samt kompilere og afvikle igen. Nu burde kompileren fortælle dig at der er noget den ikke kan finde ud af.

Du kan også prøve at lege lidt med Python - se en kort intro her.

Hvis det er første gang du har prøvet at skrive og afvikle et program så velkommen i vores verden!

Variabler

Variabler er et grundlæggende koncept i programmering, og kan findes i alle programmeringssprog. De defineres med et simpelt navn, og repræsenterer en værdi eller en reference til en værdi der er gemt i hukommelsen medens dit program bliver afviklet. Hvis du ikke havde variabler, ville du selv skulle skrive koden til få rettigheder fra operativsystemet til at bruge noget af hukommelsen, koden til at gemme eller hente en binær værdi et givet sted i hukommelsen, samt konvertere den binære værdi til noget du nemt kan bruge i din applikation.

Afhængig af programmeringssprog kan variabler navngives på forskellig måde. I nogen sprog er der ingen forskel på variabelnavne med store og små bogstaver, i nogen sprog må man ikke benytte specialtegn, og i helt gamle sprog er man begrænset i længden af et variabelnavn. De fleste sprog kræver at man først erklærer en variabel inden man kan benytte den, men der er også nogen sprog hvor det ikke er nødvendigt.

Værdien af en variabel skal i nogen sprog defineres til en bestemt type, såsom heltal, decimaltal, tekst eller endda en kompleks type som en liste eller en struktur. I andre sprog finder kompileren selv ud af det.

I et sprog som C# er der eksempelvis forskel på variabler navngivet med store og små bogstaver, alle variabler skal erklæres og tildeles en værdi før brug, og man erklære altid variabler af en konkret variabeltype. Et sådan sprog kaldes for et typestærkt sprog. Det gør det lidt besværligt at arbejde med variabler i sammenligning med typesvage sprog som eksempelvis Python, men det betyder at kompileren bedre kan holde øje med at du ikke lave dumme fejl ved tildeling og aflæsning ligesom den har nemmere ved at optimere koden ved afvikling.

Her er et simpelt eksempel på brug af tre variabler i det typesvage Python sprog:

a = 10
b = 20
c = a + b
print("The value of c is", c)

og her er samme kode i det typestærke C#:

int a = 10;
int b = 20;
int c = a + b;

Console.WriteLine("The value of c is " + c);

Bemærk forskellen på de to sprog. I Python bruges alle variablerne (a, b og c) uden nogen erklæring og definition overhovedet. I C# skal variablerne ikke bare erklæres, men de skal også erklæres af en speciel type. Python-koden er hurtigt at skrive men det nemt at lave fejl senere i programmet fordi kompileren ikke kan være sikker på hvad der egentlig er opbevaret i variablerne. I C# koden har kompileren helt styr på hvilke variabler du ønsker samt hvilken type og kan der med i langt højere grad advare dig hvis du laver fejl, og optimere programmet på flere måder.

Du skal dog ikke se Python som et sprog der er dårligere eller er væsentlig langsommere end C#. Der er mange måder at optimere typesvage sprog på så de i virkeligheden er lige så hurtige som typesvage sprog. Det handler i højere grad om den sikkerhed det giver at kompileren har bedre muligheder for at følge med i hvad du laver.

Flow

Flow-instruktioner er en del af alle programmeringssprog. De har til formål at styre hvordan en applikation afvikles ved hjælp af forskellige typer af betingelser og løkkestrukturer.

En betingelse er baseret på resultatet af en operation som enten ender i sand (true) eller falsk (false).

Her er et eksempel hvor man i en applikation ønsker at spille plat eller krone:

Brug af en if/else

Hvis spillet skulle implementeres i et visuelt programmeringssprog som Google Blocky kunne det se således ud:

Plat og krone i Google Blocky (MIT App Inventor)

Hvis spillet skulle implementeres i Python, kunne det se således ud:

import random
random_number = random.random()
if random_number < 0.5:
    result = "Heads"
else:
    result = "Tails"
print(result)

Og her er samme kode i C#:

Random random = new Random();
double random_number = random.NextDouble();
string result;
if (random_number < 0.5)
{
    result = "Heads";
}
else
{
    result = "Tails";
}
Console.WriteLine(result);

Bemærk de forskellige måder at definere betingelsen – men de ender alle sammen i det samme.

Der findes typisk flere forskellige måder at skrive en betingelse på i de forskellige programmeringssprog – C# har eksempelvis både “if” og “switch” som du skal se senere.

En anden måde at styre flowet i en applikation er løkker, som har til formål at gentage instruktioner for at undgå gentaget kode. Her er en et eksempel på en løkke i en visuel repræsentation:

En løkke i Google Blocky

Koden skaber en tælle-variabel (a) og afvikler en enkelt instruktion 10 gange. For hver gang løkken gennemløbes opskrives tællevariablen med 1.

Samme kode ser således ud i Python:

count = 1
while count <= 10:
    print(count)
    count += 1

og her i C#:

int count = 1;
while (count <= 10)
{
    Console.WriteLine(count);
    count++;
}

Koden benytter en såkaldt While-struktur til at tælle til ti, men de fleste programmeringssprog kan ”loope kode” på mange måde. Her er et andet Python eksempel:

for count in range(1, 11):
    print(count)

og her i C#:

for (int count = 1; count <= 10; count++)
{
    Console.WriteLine(count);
}

løkker og betingelser er meget vigtige for at styret flow’et i applikationen, og er at finde i alle programmeringssprog.

Prøv det selv: Gæt et tal

Uden nødvendigvis at forstå hele koden kan du prøve at skabe en konsol applikation som følger:

  • Opret en ny simpel “Hello World” applikation - se hvordan her.
  • Sørg for at koden kan kompilere og afvikles (se førnævnte guide)
  • Fjern koden i Program.cs og erstat den med
Random rand = new Random();
int secretNumber = rand.Next(1, 101);
int guess;

Console.WriteLine("Guess a number between 1 and 100");

do
{
    Console.Write("Enter your guess: ");
    guess = Convert.ToInt32(Console.ReadLine());

    if (guess < secretNumber)
    {
        Console.WriteLine("The number is higher");
    }
    else if (guess > secretNumber)
    {
        Console.WriteLine("The number is lower");
    }
    else
    {
        Console.WriteLine("You got it right!");
    }
} while (guess != secretNumber);

Kompiler og kør applikationen.

Gæt et tal afviklet fra konsol (her Windows)

Applikationen finder et tilfældigt tal mellem 1 og 100 og anmoder brugeren om at indtaste et gæt. Herefter udskrives om gættet er større eller mindre end det hemmelige tal, og det gentages indtil brugeren gætter tallet.

Bemærk brugen af en betingelse (større, mindre eller lig med) samt løkken rundt det hele.

Husk – den bedste måde at lære at programmere er at gøre det. Også selvom du bare skriver af og mangler lidt forståelse for hvorfor en syntaks netop skal skrives sådan. Det er underordnet. Få skrevet noget kode og lær af fejlene.

Tip

Brug endelig kunstig intelligens til at både skrive og forstå kode. Kopier eventuelt koden til spillet til en GPT AI, og bed den forklare, omskrive eller udvide koden. GPT står for Generative Pretrained Transformer. Det er en type af kunstig intelligens-model, der er trænet med en enorm mængde tekstdata for at generere tekst. I skrivende stund er GTP modeller tilgængelig fra OpenAI (ChatGPT), Microsoft Bing og Google Bard.

Funktioner

En anden måde at undgå gentaget kode og samtidigt gøre koden meget læsbar er brugen af funktioner. I forskellige sprog kaldes funktioner også for metoder, procedurer, eller subrutiner. Begreberne dækker konceptuelt over det samme – at kunne samle instruktioner i en blok for at kunne genbruge koden.

Nogle funktioner udfører blot en samling instruktioner, og andre funktioner kan ses som en matematisk funktion, som returnerer et decideret resultat. Slutteligt kan en funktion (men behøver ikke) tage input den skal bruges til at udføre instruktionerne. Det kaldes typisk parametre.

Her er et eksempel i visuel programmering hvor funktioner MyPrint bruges til at tælle til et givet antal som bestemmes af en parameter kaldet count.

En funktion i Google Blocky

Bemærk at funktionen afvikles (man siger også den kaldes) to gange – først med argumentet 5 og herefter med argumentet 25. Formålet med MyPrint er altså at gøre det nemt at tælle til et givet antal.

Her er samme kode i Python, hvor en funktion defineres med et def-kodeord:

def MyPrint(count):
for i in range(1, count + 1):
    print(i)

MyPrint(5)
MyPrint(25)

og her i C# som du vi lære senere:

void MyPrint(int count)
{
    for (int i = 1; i <= count; i++)
    {
        Console.WriteLine(i);
    }
}

MyPrint(5);
MyPrint(25);

Bemærk, at man i C# bruger void-kodeordet for at fortælle kompileren, at denne funktion (eller nærmere metode, som man kalder det i C#) ikke returnerer nogen værdi. Kodeordet ”void” kan oversættes til ”tom”.

Men man kan også vælge at skabe en funktion som returnerer en værdi der kan ses som et resultat. Et eksempel kunne være en funktion som lægger to tal sammen.

En funktion AddNumbers i Google Blocky

Funktionen AddNumbers kan bruges til at lægge to tal sammen og returnere et resultat.

Samme kode ser således ud i Python:

def AddNumbers(num1, num2):
    result = num1 + num2
    return result

# Example of use
sum = AddNumbers(5, 5)
print("The sum is:", sum)

Og her i C#:

int AddNumbers(int num1, int num2)
{
        int result = num1 + num2;
        return result;
}

// Example of use
int sum = AddNumbers(5, 5);
Console.WriteLine("The sum is: " + sum);

Funktioner (eller metoder som det hedder i C#) er et uundværligt værktøj i udviklingen af en applikation fordi det gør det nemt at genbruge funktionalitet.

Fejlhåndtering

Når du afvikler applikationer, vil der altid være risiko for at der sker fejl. Det kan både være fejl som skyldes forkert kode eller at applikationen afvikles i et miljø som i en eller anden form er fejlbehæftet.

Debugging

Begrebet “debugging” i programmering har en interessant historisk oprindelse. Ordet “bug” blev oprindeligt brugt for at beskrive fejl eller defekter i maskineri og udstyr. Den mest berømte anekdote, der forbindes med udtrykkets oprindelse i computerverdenen, handler om datalog og opfinder Grace Hopper. I 1940’erne, mens hun arbejdede på Harvard University’s Mark II-computer, opdagede hendes team en reel fysisk insekt – en møl – fanget i en af relæerne, hvilket forårsagede en fejl i maskinen. De fjernede insektet og noterede hændelsen som “debugging the system”, hvilket bogstaveligt talt betød at fjerne en bug (insekt) fra systemet. Selvom brugen af “bug” for at beskrive fejl i teknisk udstyr eksisterede før denne hændelse, hjalp denne beretning med at popularisere udtrykket “debugging” i forbindelse med fejlfinding og rettelse af problemer i computerprogrammer.

Eksempel på en kodefejl kunne være at “komme til” at dividere et tal med 0 hvilket er matematisk umuligt. Her er lidt C# kode som viser problemet:

int tal1 = 100;
int tal2 = 0;
int res = tal1 / tal2;

Bemærk, at beregningen vil forsøge at dividere 100 med 0, og det vil skabe en fejl.

Fejl behøver ikke nødvendigvis være baseret på kodefejl men kan skyldes fejl i det miljø applikationen afvikles i. Her eksempelvis C# kode der forsøger at læse tekst fra en fil, og hvis den ikke kan findes på disk vil det skabe en fejl:

string txt = System.IO.File.ReadAllText(@"c:\temp\data.txt");

Udfordringen med sådan en fejl er jo at den ikke nødvendigvis sker på den maskine hvor applikationen bliver udviklet. Problemet kan løses ved at kontrollere om filen findes inden den åbnes, men det løser ikke nødvendigvis alle problemer. Der kunne også ske en fejl hvis filen findes men den bruger applikationen afvikles under ikke har de nødvendige rettigheder.

En fejl hedder i de fleste moderne programmeringssprog en exception, og kan enten være unhandled (ikke håndteret) eller handled (håndteret).

At håndtere en fejl sker typisk ved at prøve noget kode af, og hvis der sker en fejl kan der afvikles noget andet kode. De fleste moderne sprog har en eller anden ”try/catch” struktur til formålet.

I Python ser det eksempelvis således ud:

try:
    # try some code
except Exception as ex:
    # handle the exception
finally:
    # execute code no matter what

I C# er det næste det samme:

try 
{
    // try some code
}
catch (Exception ex) 
{
    // handle the exception
}
finally 
{
    // execute code no matter what 
}

Begrebsmæssigt siger man typisk i de fleste moderne sprog at der bliver kastet eller smidt en exception, og at en exception bliver fanget eller håndteret.

Prøv det selv: Applikation der fejler

Uden nødvendigvis at forstå koden kan du prøve at skabe en C# konsol applikation som følger:

  • Opret en ny simpel “Hello World” applikation - se hvordan her
  • Sørg for at koden kan kompilere og afvikles (se førnævnte guide)
  • Fjern koden i Program.cs og erstat den med
int tal1 = 100;
int tal2 = 0;
int res = tal1 / tal2;

Kompiler og kør applikationen.

Applikation som smider en "Unhandled exeption" (DivideByZeroException)

Bemærk, at applikationen smider en DivideByZeroException fordi koden forsøger at dividere et tal med nul.

Hvis du har lyst kan du prøve at rette koden til følgende:

try
{
    int tal1 = 100;
    int tal2 = 0;
    int res = tal1 / tal2;
}
catch (Exception ex)
{
    // Missing log code
    Console.WriteLine("Error: " + ex.Message);
}

Nu pakkes koden ind i en try/catch som fanger en eventuel fejl, og i dette eksempel blot udskriver en ”pæn” fejlmeddelelse. Der kan dog tilføjes anden kode som eksempelvis kode til at logge fejl i en fil eller database. Log er typisk en integreret del af fejlhåndtering.

Debugging

Debugging er processen med at finde og rette fejl i en computerprogram. Når du skriver kode, er det næsten uundgåeligt, at der opstår fejl, også kaldet bugs. Debugging hjælper dig med at identificere og løse disse fejl, så dit program fungerer som forventet. For begyndere kan debugging virke skræmmende, men det er en essentiel del af programmeringsprocessen og en værdifuld færdighed at mestre.

Grundlæggende principper

  1. Identifikation af fejl: Når dit program ikke fungerer som forventet, skal du først identificere, hvad der går galt. Dette kan være alt fra syntaksfejl, som forhindrer programmet i at køre, til logiske fejl, som får programmet til at opføre sig forkert.

  2. Brug af print statements: En simpel metode til at debugge er at bruge print statements (f.eks. Console.WriteLine i C#) til at udskrive værdier af variabler på forskellige punkter i koden. Dette kan hjælpe dig med at forstå, hvad der sker i programmet og hvor det går galt.

int number = 10;
Console.WriteLine("Number is: " + number);
number += 5;
Console.WriteLine("Number after addition is: " + number);
  1. Sæt breakpoints: I en IDE kan du sætte breakpoints, som stopper programmet midt i eksekveringen på et bestemt sted i koden. Når programmet er stoppet, kan du inspicere værdier af variabler og udføre trin-for-trin eksekvering for at forstå, hvordan koden kører.

  2. Trin-for-trin eksekvering: Når programmet er stoppet ved et breakpoint, kan du bruge trin-for-trin eksekvering til at gå gennem koden linje for linje. Dette hjælper dig med at se, præcis hvordan data ændrer sig, og hvor en fejl kan opstå.

  3. Inspektion af variabler: Mens du debugger, kan du inspicere variablernes værdier for at sikre, at de indeholder de forventede data. Dette kan hjælpe dig med at opdage, hvis en variabel bliver ændret på en uventet måde.

Eksempel

Lad os sige, at du har en simpel funktion, der skal lægge to tal sammen, men resultatet er ikke som forventet:

int Add(int a, int b)
{
    return a - b; // Der er en fejl her - vi skulle bruge + i stedet for -
}

int result = Add(5, 3);
Console.WriteLine("Result: " + result); // Forventet resultat er 8, men vi får 2

For at debugge denne fejl kan du sætte et breakpoint på linjen med return-sætningen og inspicere værdierne af a og b. Når du ser, at der bruges - i stedet for +, kan du rette fejlen:

int Add(int a, int b)
{
    return a + b; // Fejlen er rettet
}

int result = Add(5, 3);
Console.WriteLine("Result: " + result); // Nu får vi det forventede resultat 8

Debugging er en kraftfuld teknik, som alle programmører bør mestre. Det hjælper dig med at forstå din kode bedre, finde og rette fejl mere effektivt, og i sidste ende skrive mere pålidelig software.

Pakkesystemer i softwareudvikling

I moderne softwareudvikling er det sjældent, at en applikation bygges helt fra bunden uden at trække på eksterne ressourcer. Dette er, hvor pakkesystemer kommer ind i billedet. Et pakkesystem er et værktøj, der gør det muligt for udviklere at administrere og integrere eksterne biblioteker og værktøjer i deres projekter. Disse eksterne ressourcer, ofte kaldet “pakker” eller “moduler”, kan indeholde alt fra simple kodebiblioteker til komplekse frameworks.

Pakkesystemer er vigtige af flere årsager:

  1. Forenkling af afhængigheder: Pakkesystemer automatiserer processen med at hente, installere og opdatere eksterne biblioteker. Dette sikrer, at alle nødvendige afhængigheder er til stede og i de korrekte versioner.
  2. Versionering: Udviklere kan specificere præcise versioner af pakker, hvilket sikrer kompatibilitet og stabilitet i applikationen.
  3. Centraliseret opbevaring: De fleste pakkesystemer har et centralt repository, hvor udviklere kan uploade og dele deres pakker.

De mest populære pakkesystemer

  • NuGet: Dette er det primære pakkesystem for .NET-udvikling. Det gør det nemt for udviklere at inkludere .NET-biblioteker og værktøjer i deres projekter.
  • pip: Dette er pakkesystemet for Python. Med pip kan Python-udviklere nemt installere og administrere biblioteker og værktøjer fra Python Package Index (PyPI).
  • npm: Node Package Manager er det mest populære pakkesystem for JavaScript, især for Node.js-udvikling.
  • Maven/Gradle: Disse er populære pakkesystemer inden for Java-verdenen, hvor Maven primært fokuserer på livscyklusstyring og Gradle tilbyder en fleksibel byggeautomatisering.

I takt med at softwareudviklingslandskabet fortsætter med at udvikle sig, vil pakkesystemer fortsat spille en afgørende rolle i at sikre, at udviklere kan bygge på andres arbejde, samtidig med at de opretholder konsistens og stabilitet i deres egne projekter.

Programmeringsparadigmer

Programmeringsparadigmer er forskellige tilgange eller stilarter til at skrive kode. De definerer, hvordan programmeringsproblemer tænkes over og løses. Her er nogle af de mest almindelige paradigmer:

Imperativ Programmering

I denne stil beskriver man trin-for-trin, hvad computeren skal gøre. Det er som at give en detaljeret opskrift. De allerførste programmeringssprog var imperativt orienterede.

Eksempel: assembler og C.

Procedural Programmering

Dette er en underkategori af imperativ programmering, hvor der fokuseres på at opdele kode i mindre procedurer eller funktioner. Procedural programmering gør det lettere at organisere og genbruge kode.

Eksempel: COBOL, Pascal, VBA, Java, C#

Deklarativ Programmering

I stedet for at beskrive, hvordan noget skal gøres, beskriver man, hvad man ønsker at opnå, og lader systemet finde ud af, hvordan det gøres. Deklarativ programmering er nyttig i situationer, hvor det er mere vigtigt at beskrive resultatet end processen.

Eksempel: SQL (for databaser) og CSS (for styling af websider)

Objektorienteret Programmering (OOP)

Her grupperes data og funktioner i objekter. Dette paradigme fokuserer på genbrug af kode og modellering af virkelige verdens objekter. OOP understøtter nøglebegreber som arv, polymorfi og indkapsling, hvilket gør det muligt at bygge fleksible og vedligeholdelsesvenlige systemer.

Eksempel: Java, C#

Funktionel Programmering

I denne stil behandles beregninger som en række matematiske funktioner. Det undgår at ændre tilstand og mutable data, hvilket gør funktionel programmering særligt egnet til parallelle og asynkrone applikationer. Funktionelle programmeringssprog understøtter højere ordens funktioner og rekursion som centrale koncepter.

Eksempel: Haskell, Lisp

Logisk Programmering

Dette paradigme er baseret på formel logik. Man beskriver relationer mellem objekter og lader systemet udføre deduktioner. Logisk programmering anvendes ofte i kunstig intelligens og problemløsning, hvor regler og fakta er defineret, og systemet finder svar baseret på disse regler.

Eksempel: Prolog

Hændelsesdrevet Programmering

Her skrives kode, der reagerer på eksterne hændelser eller brugerinput, som ofte bruges i grafiske brugerflader og realtidsapplikationer. Hændelsesdrevet programmering er centralt i udviklingen af interaktive systemer, hvor handlinger udløses af brugerinput, systembegivenheder eller beskeder fra andre applikationer.

Eksempel: JavaScript (i webudvikling), Windows Forms (i C#), Node.js

Asynkron Programmering

Asynkron programmering gør det muligt at udføre operationer, der tager tid (som netværksanmodninger eller filsystemoperationer) uden at blokere hovedprogramflowet. Dette forbedrer applikationens reaktivitet og ydeevne, især i miljøer, hvor mange opgaver skal håndteres samtidigt. Asynkron programmering bruger ofte begreber som callbacks, løfter (promises) og fremtidige (futures) for at håndtere asynkrone operationer. I C# gør async/await det muligt at skrive asynkron kode på en måde, der ligner synkron kode, hvilket gør den lettere at læse og vedligeholde.

Eksempel: Async/await i C# og JavaScript.

Ved at forstå og anvende de forskellige programmeringsparadigmer kan man vælge den mest passende tilgang til forskellige typer af problemer, hvilket fører til mere effektiv og vedligeholdelsesvenlig kode.

Algoritmer

En algoritme kan forstås som en samling instruktioner eller trin til at løse et problem eller udføre en opgave. Vi bruger algoritmer i vores daglige liv, ofte uden at være bevidste om det. Tag for eksempel din morgenrutine, som inkluderer at stå op, børste tænder og spise morgenmad. Hvert trin i denne rutine er en del af en ‘algoritme’ for at starte din dag.

I computerverdenen bliver algoritmer mere komplekse og kræver præcision. De fortæller computeren, hvordan den skal udføre specifikke opgaver. Et enkelt eksempel er processen med at lave en peanut butter sandwich, hvor hvert trin skal beskrives nøjagtigt: åbning af brødposen, tagning af to skiver brød, smøring af peanut butter på den ene skive, og så videre. Hvis instruktionerne ikke er præcise og i den rigtige rækkefølge, vil det resultere i fejl – det samme gælder i computerprogrammering.

Et klassisk eksempel på en algoritme fra den fysiske verden er den måde, man tidligere fandt navne i en telefonbog. I stedet for at gennemgå hver side en efter en (en lineær søgning), kunne man bruge en mere effektiv metode, som at åbne bogen på midten og afgøre, om man skulle søge til venstre eller højre (en form for binær søgning). Denne form for at opdele problemet i mindre dele og løse hver del effektivt er essensen i mange computer algoritmer.

I programmering er præcision afgørende. En computer vil kun udføre de instruktioner, den får, og kan ikke selv rette fejl eller gætte sig til manglende trin. Derfor skal hver detalje i en algoritme være korrekt specificeret. Et andet eksempel på en algoritme i programmering er ‘bubblesort’, hvor tal eller elementer bliver sorteret ved gentagne gange at sammenligne naboelementer og ombytte dem, hvis de er i den forkerte rækkefølge. Denne proces fortsætter, indtil ingen elementer skal ombyttes, og listen er sorteret. Selvom denne metode er simpel, illustrerer den vigtigheden af trinvise processer og præcision i algoritmisk tænkning.

Muligheder i Programmering

Programmering spænder over en bred vifte af applikationer og platforme. Her er nogle af de mest almindelige områder, hvor programmering anvendes.

Konsolapplikationer

Dette er tekstbaserede programmer, der kører i kommandoprompten eller terminalen. De er ofte hurtige at udvikle og bruges til scripts og værktøjer. Eksempel: Bash scripts, Python scripts

Desktopapplikationer

Dette er programmer, der kører på en computers skrivebord. De kan have grafiske brugerflader og udnytte computerens fulde ressourcer. Eksempel: Microsoft Word, Photoshop

Webapplikationer

Programmer, der kører i en webbrowser. De kan tilgås fra enhver enhed med en browser og internetforbindelse. Eksempel: Facebook, Google Docs

Mobilapplikationer

Applikationer designet specifikt til mobile enheder som smartphones og tablets. Eksempel: Instagram, MobilePay

RESTful Services

REST (Representational State Transfer) er en arkitektonisk stil, der definerer et sæt begrænsninger og egenskaber baseret på HTTP. RESTful services, ofte blot kaldet RESTful APIs, er webtjenester, der implementerer REST-principperne.

  • Stateless: Hver anmodning fra en klient til en server skal indeholde alle de oplysninger, der er nødvendige for at forstå og behandle anmodningen. Serveren må ikke gemme kontekstinformation mellem anmodninger.

  • Client-Server: RESTful services følger en client-server model, hvor client og server opererer uafhængigt. Dette adskiller brugergrænsefladen fra dataopbevaring, hvilket gør det muligt at ændre dem uafhængigt af hinanden.

  • Cacheable: Responsdata skal kunne caches på klienten. Dette kan forbedre ydeevnen ved at reducere genanvendelige anmodninger.

  • Uniform Interface: For at forenkle interaktionen mellem klient og server, skal RESTful services have et ensartet interface, som ofte er defineret ved hjælp af standardiserede metoder som GET, POST, PUT og DELETE.

Mikroservices

Mikroservices er en arkitektonisk stil, hvor en applikation er sammensat af små, uafhængige services, der kører i deres egne processer og kommunikerer med letvægtsmekanismer, ofte over HTTP. Disse tjenester er bygget omkring forretningsfunktioner og kan udvikles, implementeres og skaleres uafhængigt.

Microprocessorer og Indlejret Software

Dette er kode, der kører på specialiserede chips eller indlejrede systemer, ofte uden en traditionel OS. Eksempel: Firmware på en router, software i en vaskemaskine

Fjernsyn og Medieafspillere

Software, der driver moderne smart-tv’er og streamingenheder, giver brugerne mulighed for at se indhold og interagere med apps. Eksempel: Netflix-appen på et smart-tv, Roku medieafspiller

Spiludvikling

Udvikling af interaktive spil, der kan spilles på forskellige platforme. Eksempel: Fortnite, Minecraft

Hver af disse muligheder har sine egne udfordringer og krav, men de giver også programmører mulighed for at skabe en bred vifte af spændende og nyttige produkter.

Roller i Softwareudvikling

I softwareudvikling spiller forskellige roller en afgørende rolle for at sikre, at et projekt bliver succesfuldt. Her er en oversigt over nogle af de mest almindelige roller:

Projektleder (PM)

En projektleder (Project Manager - PM) er ansvarlig for at styre projektet fra start til slut. De planlægger, organiserer og overvåger alle aspekter af projektet, herunder tidsplaner, budgetter og ressourcer. PM’en sikrer, at teamet arbejder effektivt og opfylder målene inden for de fastsatte tidsrammer.

Frontend-udvikler

Frontend-udviklere fokuserer på den del af applikationen, som brugerne interagerer med. De arbejder med teknologier som HTML, CSS og JavaScript for at skabe en brugervenlig og æstetisk tiltalende grænseflade. Frontend-udviklere sørger for, at applikationen fungerer godt på forskellige enheder og browsere.

Backend-udvikler

Backend-udviklere arbejder med den server-side logik, der driver applikationen. De udvikler og vedligeholder databaser, servere og applikationslogik ved hjælp af programmeringssprog som C#, Java eller Python. Backend-udviklere sikrer, at data flyder korrekt mellem frontend og backend, og at applikationen er skalerbar og sikker.

UI Designer

UI (User Interface) designere fokuserer på udseendet og følelsen af en applikation. De skaber layout, farveskemaer, typografi og ikoner, der sikrer en sammenhængende og attraktiv brugeroplevelse. UI designere arbejder tæt sammen med frontend-udviklere for at sikre, at designet bliver korrekt implementeret.

UX Designer

UX (User Experience) designere har til opgave at forbedre den samlede brugeroplevelse. De udfører brugerundersøgelser, skaber wireframes og prototyper, og tester applikationens brugervenlighed. UX designere arbejder på at sikre, at applikationen er intuitiv og opfylder brugernes behov og forventninger.

Tester

Testere, også kendt som QA (Quality Assurance) specialister, spiller en vigtig rolle i at sikre, at applikationen er fejlfri og fungerer som forventet. De udvikler og udfører testplaner, herunder enhedstest, integrationstest og brugeraccepttest. Testere arbejder tæt sammen med udviklingsteamet for at identificere og rette fejl, samt sikre at alle krav er opfyldt.

Disse roller arbejder ofte tæt sammen og er afhængige af hinanden for at levere et vellykket softwareprodukt. Hver rolle bidrager med sin unikke ekspertise og perspektiv, hvilket hjælper med at skabe en holistisk og funktionel løsning.

Versionsstyring

Når man arbejder på større projekter - især i teams - er det vigtigt at have en metode til at spore ændringer i koden og samarbejde med andre. Versionsstyringssystemer som Git giver udviklere mulighed for at tage “snapshots” af deres kode, samarbejde med andre og flette ændringer sammen.

Hvad er versionsstyring?

Versionsstyring er en metode til at holde styr på ændringer i dokumenter, programmeringskode og andre samlinger af information. Det giver mulighed for at gendanne tidligere versioner af et projekt, analysere historiske ændringer og samarbejde effektivt i teams. Med versionsstyring kan man undgå konflikter og tab af data, som kan opstå, når flere personer arbejder på det samme projekt samtidig.

Git

Git er et populært distribueret versionsstyringssystem opfundet af Linus Torvalds. Det bruges til at spore ændringer i kildekoden under udvikling. Git giver hver udvikler en fuld kopi af hele historikken for projektet, hvilket muliggør effektiv branching, merging og muligheden for at arbejde offline. Git gør det nemt at arbejde sammen, da man kan flette ændringer fra forskellige udviklere uden at miste noget arbejde.

Læs mere her.

GitHub

GitHub er en webbaseret platform bygget oven på Git, som tilbyder versionsstyring og samarbejdsværktøjer til udviklere. Det giver et grafisk interface, hvor man kan se, dele og samarbejde om projekter. GitHub tilbyder også funktioner som pull requests, issues, og projektstyring, hvilket gør det lettere at administrere og bidrage til open source-projekter. GitHub bruges af millioner af udviklere verden over til at hoste og gennemgå kode, administrere projekter og bygge software sammen.

Azure DevOps

Azure DevOps er en samling af udviklingsværktøjer fra Microsoft, som understøtter hele udviklingscyklussen, fra planlægning til kodning, bygging, testning og deployering. Det inkluderer funktioner som Git-repositories, CI/CD pipelines, boards til projektstyring, og testplanlægning. Azure DevOps integreres problemfrit med andre Microsoft-produkter og -tjenester, hvilket gør det til et kraftfuldt værktøj for teams, der arbejder i et Microsoft-økosystem.

Ved at bruge versionsstyringsværktøjer som Git, platforme som GitHub og integrerede løsninger som Azure DevOps, kan udviklere og teams arbejde mere effektivt og samarbejde bedre, hvilket resulterer i mere pålidelige og veldokumenterede softwareprojekter.

Databaser

Data er hjertet i mange applikationer. At forstå, hvordan man opbevarer, henter og manipulerer data, er afgørende. Databaser som SQL Server, MySQL eller MongoDB giver mulighed for at opbevare data på en struktureret måde og hente dem effektivt gennem forespørgsler. Læs mere her.

Relationsdatabaser

Relationsdatabaser organiserer data i tabeller, der er forbundet gennem relationer. De bruger SQL (Structured Query Language) til at definere og manipulere data. Eksempler på relationsdatabaser inkluderer:

  • SQL Server: En relationel databaseudviklet af Microsoft, der understøtter en bred vifte af transaktionsbehandlings-, business intelligence- og analytiske applikationer.
  • MySQL: En open source relationsdatabase, der er kendt for sin hastighed og pålidelighed, ofte brugt i webapplikationer og online-tjenester.
  • PostgreSQL: En avanceret open source relationsdatabase, der er kendt for sin omfattende support til avancerede datatyper og ydeevneoptimering.

Objektdatabaser

Objektdatabaser gemmer data i form af objekter, som de defineres i objektorienterede programmeringssprog. Dette gør det nemmere at lagre komplekse data strukturer, der matcher de objekter, der bruges i applikationerne. Eksempler på objektdatabaser inkluderer:

  • MongoDB: En NoSQL database, der gemmer data i JSON-lignende dokumenter, hvilket giver en fleksibel og skalerbar måde at håndtere data på.
  • ObjectDB: En ren Java-database, der er designet til at fungere som en højtydende objektdatabase for Java-applikationer.

Ved at vælge den rette type database og forstå dens styrker og begrænsninger kan man sikre, at dataene opbevares, hentes og manipuleres på den mest effektive måde for den givne applikation.

Docker og containerisering

I takt med at applikationer bliver mere komplekse, bliver behovet for at sikre, at de kører ensartet på tværs af forskellige miljøer, mere presserende. Docker og andre container-teknologier giver udviklere mulighed for at “pakke” deres applikationer sammen med alle deres afhængigheder, hvilket sikrer konsistens uanset, hvor applikationen kører. Læs mere her.

Testning

I enhver softwareudviklingsproces er testning afgørende for at sikre, at softwaren fungerer korrekt og opfylder de specificerede krav. Testning hjælper med at identificere fejl, mangler eller uoverensstemmelser i forhold til de forventede resultater.

  • Unit Tests: Disse tester enkelte dele af koden isoleret fra resten (f.eks. en funktion eller metode). Formålet er at bekræfte, at hver enkelt enhed fungerer som forventet.

  • Integration Tests: Disse tester interaktionen mellem flere enheder eller komponenter for at sikre, at de arbejder godt sammen.

  • End-to-End Tests: Disse tester hele applikationen som en helhed, ofte fra en brugers perspektiv, for at sikre, at hele systemet fungerer som forventet.

  • Continuous Integration (CI): CI refererer til praksis med automatisk at integrere kodeændringer fra flere bidragydere i et softwareprojekt. Hovedideen er at sikre, at nye kodeændringer ikke bryder eksisterende funktionalitet. Dette opnås ved automatisk at køre tests hver gang kode tilføjes eller ændres.

Ressourcer

Der er et hav af ressourcer tilgængelige for at hjælpe dig med at lære at programmere. Her er nogle af de mest populære:

Du kan finde flere ressourcer her.

Tip

Kig på https://www.edx.org/cs50 fra Harvard University. Det er en gratis online introduktion til programmering, der dækker mange af de emner, der er nævnt i denne guide.

Klar til C#

Det var en kort introduktion til nogle af de begreber du vil benytte i alle programmeringssprog, så nu er du klar til at kaste dig over C#.  

Appendisk: Mulige interessante spillefilm og dokumentarudsendelser

Her er nogle spillefilm om emnet (jeg har markeret nogle af dem som must see)

  • “WarGames” (1983): En ung mand ved et uheld næsten starter en atomkrig, efter at have hacket sig ind i en militær supercomputer. IMDB Link Filmstriben

  • “Tron” (1982): En computerprogrammør bliver transporteret ind i en digital verden inde i en computer. IMDB Link

  • “Hackers” (1995): Fokuserer på en gruppe unge hackere og deres eventyr, der giver et indblik i den tidlige hackerkultur. IMDB Link

  • 👍 “Pirates of Silicon Valley” (1999): Skildrer den tidlige historie bag Apple og Microsoft. IMDB Link

  • “The Social Network” (2010): Fortæller historien om Facebooks skabelse og dens grundlægger, Mark Zuckerberg. IMDB Link

  • “Tron: Legacy” (2010): En opfølger til originalen, der igen bringer helten ind i den digitale verden. IMDB Link

  • 👍 “The Imitation Game” (2014): Skildrer livet og arbejdet for Alan Turing, der er kendt for sit arbejde med at bryde den tyske Enigma-kode under Anden Verdenskrig. IMDB Link og Filmstriben

  • 👍 “Her” (2013): Skildrer en nær fremtid, hvor kunstig intelligens er avanceret nok til at danne dybe, personlige forhold til mennesker. IMDB Link

  • “Ex Machina” (2014): En provokerende film om kunstig intelligens og dens mulige konsekvenser. IMDB Link

  • 👍 “Steve Jobs” (2015): Fokuserer på tre bestemte punkter i Jobs’ karriere, herunder lanceringsbegivenhederne for Macintosh i 1984, NeXT Computer i 1988 og iMac i 1998. IMDB Link

  • 👍 “Hidden Figures” (2016): Skildrer de afroamerikanske kvinder, der arbejdede som “menneskelige computere” ved NASA i 1960’erne. IMDB Link

  • 👍 “Silicon Valley” (2014-2019): Selvom det er en tv-serie og ikke en film, skildrer den livet i en moderne tech-startup i Silicon Valley på en humoristisk og indsigtsfuld måde. IMDB Link

Her er lidt dokumentarudsendelser - de kan sikkert findes rundt på nettet (jeg har markeret nogle af dem som must see)

  • “The Code” (2011): Denne BBC-dokumentarserie udforsker, hvordan matematiske mønstre inspirerer og påvirker vores liv og teknologier. IMDB Link

  • 👍 “Transistorized!” (1999): Denne PBS-dokumentar detaljeret beskriver opfindelsen af transistoren og dens indflydelse på teknologien. PBS Link

  • “The Secret Rules of Modern Living: Algorithms” (2015): En BBC-dokumentar, der forklarer algoritmer og hvordan de påvirker vores moderne liv. IMDB Link

  • “The Internet’s Own Boy: The Story of Aaron Swartz” (2014): Dokumentaren omhandler livet og arbejdet hos Aaron Swartz, en programmerer og aktivist, der var medvirkende til at skabe RSS og Reddit. IMDB Link

  • “Silicon Cowboys” (2016): Denne dokumentar fortæller historien om Compaq Computer og dens indflydelse på PC-revolutionen i 1980’erne. IMDB Link

  • 👍 “Lo and Behold, Reveries of the Connected World” (2016): En dokumentar af Werner Herzog, der udforsker internettets fortid, nutid og fremtid. IMDB Link

  • “Code: Debugging the Gender Gap” (2015): Dokumentaren fremhæver manglen på kvinder og minoriteter i software engineering. IMDB Link

  • “Revolution OS” (2001): Denne dokumentar skildrer historien om open source-software og Linux. IMDB Link

  • “Inside the Mind of Google” (2009): En CNBC-dokumentar, der ser ind i Google, hvordan det fungerer og dets indflydelse på vores liv. IMDB Link

  • “The Virtual Revolution” (2010): Denne BBC-dokumentarserie undersøger, hvordan internettet har ændret vores liv i løbet af de sidste 20 år. IMDB Link

  • 👍 1969 Moon Landing: The Code of the Apollo 11 Guidance Computer (AGC) Super gennemgang af teknikken og koden bag Apollo 11 Guiance Module. Se Pluralsight.

Andre forslag imødeses gerne