Eksamen: REA3049-JS | Semester: Høst 2024 | Tema: OOP, palindrom-test, batteri-klasse, velferdsteknologi, befolkningsdatasett, virusspredning
Arv er mekanismen som gjør at en subklasse arver egenskaper og metoder fra superklassen — ren gjenbruk. I JavaScript bruker man class B extends A.
Når objektet «eier» de andre objektene som felt og de eide normalt ikke eksisterer uten den ytre helheten, er det komposisjon.
Trace: −1+2−3+4−5+6−7 = −4.
IF temperatur GREATER THAN 25 DISPLAY "Det er varmt." ELSE IF temperatur GREATER THAN OR EQUAL TO 10 DISPLAY "Det er mildt." ELSE IF temperatur GREATER THAN OR EQUAL TO 0 DISPLAY "Det er kjølig." ELSE DISPLAY "Det er kaldt." ENDIF
Algoritmen sjekker om et tall er et palindrom (leses likt forfra og bakfra). Den bygger opp den reverserte versjonen av tallet i s ved å plukke siste siffer (h % 10), legge det til som ny siste siffer i s (s * 10 + r), og fjerne sist siffer fra h. Til slutt sammenlignes original (t) med revers (s). 121 → 121 (True), 123 → 321 (False).
// oppgave5b.mjs — antall tresifrede palindromer
function erPalindrom(h) {
let t = h, s = 0;
while (h !== 0) {
s = s * 10 + (h % 10);
h = Math.floor(h / 10);
}
return t === s;
}
let antall = 0;
for (let n = 100; n < 1000; n++) if (erPalindrom(n)) antall++;
console.log(`Antall tresifrede palindromer: ${antall}`);
// Forventet svar: 90
Resultat: 90 tresifrede palindromer (101, 111, 121, …, 999). Matematisk: 9 valg for første/siste siffer × 10 for midten = 90.
// batteri.mjs — energilager med privat tilstand (# = privat felt i moderne JS)
export class BatteriFeil extends Error {
constructor(melding) { super(melding); this.name = "BatteriFeil"; }
}
export class Batteri {
#kapasitet;
#energinivaa;
constructor(kapasitet, startNiva = 0) {
if (kapasitet <= 0) throw new BatteriFeil("Kapasiteten må være positiv.");
if (startNiva < 0 || startNiva > kapasitet)
throw new BatteriFeil(`Startnivå (${startNiva}) må være mellom 0 og ${kapasitet}.`);
this.#kapasitet = kapasitet;
this.#energinivaa = startNiva;
}
lad(energi) {
if (energi < 0) throw new BatteriFeil("Kan ikke lade med negativ energi.");
this.#energinivaa = Math.min(this.#energinivaa + energi, this.#kapasitet);
}
bruk(energi) {
if (energi < 0) throw new BatteriFeil("Kan ikke bruke negativ energi.");
if (energi > this.#energinivaa)
throw new BatteriFeil(`Ikke nok energi: forsøkt ${energi}, tilgjengelig ${this.#energinivaa}.`);
this.#energinivaa -= energi;
}
visStatus() {
const prosent = (this.#energinivaa / this.#kapasitet * 100).toFixed(0);
console.log(`Batteri: ${this.#energinivaa.toFixed(1)} / ${this.#kapasitet.toFixed(1)} kWh (${prosent} %)`);
}
}
// Tester
function testBatteri() {
const b = new Batteri(10, 5);
b.visStatus(); // 5.0 / 10.0 (50%)
b.lad(3); b.visStatus();
b.lad(5); b.visStatus(); // cap til 10
b.bruk(7.5); b.visStatus();
try { b.bruk(10); } catch (e) { console.log(`OK – fanget: ${e.message}`); }
try { b.lad(-1); } catch (e) { console.log(`OK – fanget: ${e.message}`); }
try { new Batteri(-5); } catch (e) { console.log(`OK – fanget: ${e.message}`); }
console.log("Alle tester passert.");
}
testBatteri();
Identifiserte feil/unntak: (1) Bruk av mer energi enn tilgjengelig. (2) Negativ ladnings- eller bruksverdi. (Unntak) Ugyldige verdier ved opprettelse. Alle håndtert ved BatteriFeil. #kapasitet og #energinivaa er private felter (moderne JS) — ekte innkapsling, ikke konvensjon med understrek.
Velferdsteknologi — fallsensorer, GPS-armbånd, automatiske medisindispensere, robotkjæledyr og videokonsultasjon — gir både muligheter og dilemmaer i eldreomsorgen.
Fordeler. Teknologien kan øke selvstendigheten og tryggheten til eldre som bor hjemme. Fallsensorer varsler hjelp raskt, GPS-løsninger lar personer med demens bevege seg utendørs uten konstant tilsyn. Effektivt brukt frigjør teknologien tid for helsepersonell til oppgaver som krever menneskelig nærvær.
Etiske dilemmaer.
Konklusjon. Velferdsteknologi bør innføres etter prinsippet «teknologi som verktøy, ikke erstatning». Den krever solid juridisk rammeverk, opplæring av brukere og pleiere, og bevisst arbeidsdeling.
// oppgave9.mjs — befolkningsutvikling 1945–2024
/* Forberedelse av datasettet:
* - Datasettet inneholder årlige tall for fødsler, innflyttinger og utflyttinger.
* - Numeriske kolonner konverteres til tall (vi fjerner mellomrom som tusenskille).
* - Netto folkevekst beregnes som fødsler + innflyttinger − utflyttinger.
*/
import { readFileSync, writeFileSync } from "node:fs";
import readline from "node:readline/promises";
import { stdin as input, stdout as output } from "node:process";
function lesData(filsti = "befolkning.csv") {
const linjer = readFileSync(filsti, "utf-8").trim().split(/\r?\n/);
const [head, ...rest] = linjer;
const headers = head.split(";");
return rest.map(linje => {
const v = linje.split(";");
const r = Object.fromEntries(headers.map((h, i) => [h, v[i]]));
r["år"] = Number(r["år"]);
for (const kol of ["fødselstall", "innflyttinger", "utflyttinger"]) {
r[kol] = Number(r[kol].replace(/ /g, ""));
}
r["netto folkevekst"] = r["fødselstall"] + r["innflyttinger"] - r["utflyttinger"];
return r;
});
}
function visTabell(data) {
console.log(`\n${"År".padEnd(6)}${"Fødte".padStart(10)}${"Inn".padStart(10)}${"Ut".padStart(10)}${"Netto".padStart(12)}`);
console.log("-".repeat(48));
for (const r of data) {
console.log(
String(r["år"]).padEnd(6) +
r["fødselstall"].toLocaleString("nb-NO").padStart(10) +
r["innflyttinger"].toLocaleString("nb-NO").padStart(10) +
r["utflyttinger"].toLocaleString("nb-NO").padStart(10) +
r["netto folkevekst"].toLocaleString("nb-NO").padStart(12)
);
}
}
// Linjediagram som SVG
function tegnDiagram(data, kolonne, fra, til) {
const valgte = data.filter(r => r["år"] >= fra && r["år"] <= til);
if (valgte.length === 0) { console.log("Ingen data."); return; }
const bredde = 800, hoyde = 400, padding = 60;
const verdier = valgte.map(r => r[kolonne]);
const minV = Math.min(...verdier), maksV = Math.max(...verdier);
const xSteg = (bredde - padding * 2) / (valgte.length - 1);
const punkter = valgte.map((r, i) => {
const x = padding + i * xSteg;
const y = hoyde - padding - ((r[kolonne] - minV) / (maksV - minV || 1)) * (hoyde - padding * 2);
return `${x.toFixed(1)},${y.toFixed(1)}`;
}).join(" ");
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${bredde}" height="${hoyde}">
<text x="${bredde/2}" y="30" text-anchor="middle" font-size="18" font-weight="bold">${kolonne} ${fra}–${til}</text>
<polyline points="${punkter}" fill="none" stroke="#1976D2" stroke-width="2"/>
</svg>`;
writeFileSync(`diagram_${kolonne.replace(/ /g, "_")}_${fra}_${til}.svg`, svg);
console.log(`Diagram lagret som SVG.`);
}
async function main() {
const data = lesData();
visTabell(data);
const rl = readline.createInterface({ input, output });
const kolonne = (await rl.question("\nKolonne (fødselstall / innflyttinger / utflyttinger / netto folkevekst): ")).trim().toLowerCase();
const fra = parseInt(await rl.question("Fra år: "), 10);
const til = parseInt(await rl.question("Til år: "), 10);
rl.close();
if (!["fødselstall", "innflyttinger", "utflyttinger", "netto folkevekst"].includes(kolonne)) {
console.log("Ugyldig kolonne."); return;
}
tegnDiagram(data, kolonne, fra, til);
}
main();
<!DOCTYPE html>
<html lang="no">
<head><meta charset="utf-8"><title>Virusspredning</title></head>
<body style="text-align:center;font-family:sans-serif">
<h2>Virusspredning</h2>
<canvas id="lerret" width="560" height="560" style="border:1px solid #999"></canvas>
<p id="status"></p>
<script>
const RUTER = 40, CELLE = 14;
const DAGER_SMITTET = 3, DAGER_SYK = 4;
const DOD_PROB = 0.01, SMITTE_PROB = 0.3, SMITTE_RADIUS = 2;
const T = { FRISK: 0, SMITTET: 1, SYK: 2, IMMUN: 3, DOD: 4 };
const FARGER = ["#cccccc", "#f48fb1", "#e53935", "#424242", "#000000"];
class Person {
constructor(r, k) {
this.r = r; this.k = k;
this.tilstand = T.FRISK;
this.dager = 0;
}
smitt() { if (this.tilstand === T.FRISK) { this.tilstand = T.SMITTET; this.dager = 0; } }
nesteDag() {
if ([T.FRISK, T.IMMUN, T.DOD].includes(this.tilstand)) return;
this.dager++;
if (this.tilstand === T.SMITTET && this.dager >= DAGER_SMITTET) {
this.tilstand = T.SYK; this.dager = 0;
} else if (this.tilstand === T.SYK) {
if (Math.random() < DOD_PROB) this.tilstand = T.DOD;
else if (this.dager >= DAGER_SYK) { this.tilstand = T.IMMUN; this.dager = 0; }
}
}
}
class Populasjon {
constructor(rader, kolonner, startSmittet) {
this.rader = rader; this.kolonner = kolonner;
this.personer = Array.from({length: rader}, (_, r) =>
Array.from({length: kolonner}, (_, k) => new Person(r, k)));
for (let i = 0; i < startSmittet; i++) {
this.personer[Math.floor(Math.random() * rader)]
[Math.floor(Math.random() * kolonner)].smitt();
}
}
naboer(p) {
const result = [];
for (let r = Math.max(0, p.r - SMITTE_RADIUS); r <= Math.min(this.rader - 1, p.r + SMITTE_RADIUS); r++)
for (let k = Math.max(0, p.k - SMITTE_RADIUS); k <= Math.min(this.kolonner - 1, p.k + SMITTE_RADIUS); k++) {
if (r !== p.r || k !== p.k) result.push(this.personer[r][k]);
}
return result;
}
enDag() {
const smittebarere = this.personer.flat().filter(p => p.tilstand === T.SMITTET || p.tilstand === T.SYK);
for (const p of smittebarere)
for (const n of this.naboer(p))
if (Math.random() < SMITTE_PROB) n.smitt();
this.personer.flat().forEach(p => p.nesteDag());
}
statistikk() {
const teller = [0, 0, 0, 0, 0];
this.personer.flat().forEach(p => teller[p.tilstand]++);
return teller;
}
}
const pop = new Populasjon(RUTER, RUTER, 5);
const ctx = document.getElementById("lerret").getContext("2d");
const statusEl = document.getElementById("status");
let dag = 0;
function tegn() {
for (let r = 0; r < pop.rader; r++)
for (let k = 0; k < pop.kolonner; k++) {
ctx.fillStyle = FARGER[pop.personer[r][k].tilstand];
ctx.fillRect(k * CELLE, r * CELLE, CELLE, CELLE);
}
const [F, S, Sy, I, D] = pop.statistikk();
statusEl.textContent = `Dag ${dag} | Frisk ${F} Smittet ${S} Syk ${Sy} Immun ${I} Død ${D}`;
}
function loop() {
pop.enDag(); dag++;
tegn();
setTimeout(loop, 200);
}
tegn(); setTimeout(loop, 500);
</script></body></html>
Person (Del 1 — tilstandsmaskin), Populasjon (Del 2 — smittedynamikk), HTML-skript (Del 3 — visning).123456/
├── oppgave5b/palindrom.mjs
├── oppgave6/batteri.mjs
├── oppgave9/
│ ├── oppgave9.mjs
│ └── befolkning.csv
├── oppgave10/virusspredning.html
└── README.md (Node.js 18+ for ESM og private felter)