8 minutes
Esercizi liberi lezioni 18-25
Per completezza, si rimanda al primo post del sito per ulteriori informazioni.
Si ricorda che dello stesso esercizio possono esistere più varianti.
Fattori
Si scriva una funzione fattori(n) che, dato come argomento un intero positivo n, restituisca un array contenente tutte le coppie di divisori di n, incluse le coppie (1,n) e (n,1), in ordine di primo elemento crescente. Ciascuna coppia è a sua volta rappresentata come un array. Esempi
fattori(359) → [ [1,359], [359, 1] ]
fattori(10) → [ [1,10], [2,5], [5,2], [10,1] ]
fattori(12) → [ [1, 12], [2, 6], [3, 4], [4, 3], [6, 2], [12,1] ]
function fattori(n) {
var r1 = [], r2 = []
for (var i = 1; i <= Math.sqrt(n); i++)
if (n % i == 0) {
r1.push([i, n / i])
if (i != n / i)
r2.unshift([n / i, i])
}
return [...r1, ...r2]
}
// Esercizio fattori, versione con generatore
function* genfattori(n) {
for (var i = 0; i <= n; i++)
if (n % i == 0)
yield [i, n / i]
}
Öffentliche Ordnung
Si scriva una funzione ordnung(a) che, dato come argomento un array a, i cui elementi sono a loro volta array di numeri, ordini lo stesso a in modo che ogni singolo elemento sia ordinato in ordine numerico crescente, e che a sia ordinato in base al valore numerico crescente del primo elemento di ciascun elemento; a parità si passa a confrontare il secondo elemento, e così via. In caso di parità di tutti gli elementi, l’array più corto va ordinato prima di quello più lungo. Questo è un esempio di ordinamento lessicografico, perché viene usato, per esempio nei dizionari, per ordinare le parole di una lingua. Esempi:
ordnung([[5,1,3],[1,5,2,7],[3,4],[3]]) → [[1,2,5,7],[1,3,5],[3],[3,4]]
ordnung([[5,7,3],[8,3,5],[1],[5]]) → [[1],[3,5,7],[3,5,8],[5]]
function ordnung(a) {
function cmpA(aa, bb) {
var t = Math.min(aa.length, bb.length)
for (var h = 0; h < t; h++)
if (aa[h] != bb[h])
return aa[h] - bb[h]
return (aa.length - bb.length)
}
for (k in a)
a[k].sort((a, b) => (a - b))
return a.sort(cmpA)
}
Strafatto (complicato!)
Si scriva una funzione strafatto(n) che, dato come argomento un intero positivo n, restituisca un array contenente tutti i modi diversi di fattorizzare n, con divisori maggiori di 1 e minori di n. Ciascuna sequenza di divisori è a sua volta rappresentata come un array. Suggerimenti: notate che non ci limitiamo ai fattori primi, e ricordate che la ricorsione è vostra amica! Esempi:
strafatto(359) → []
strafatto(10) → [[2,5]]
strafatto(12) → [[2,2,3],[2,6],[3,4]]
strafatto(60) → [[2,2,3,5],[2,2,15],[2,30],[2,6,5],[2,3,10],… ]
// restituisce tutti i divisori di un numero (compreso esso stesso)
function divisors(n) {
var r = []
for (var d = 2; d <= n; d++)
if (n % d == 0) r.push(d)
return r
}
// enumera tutti i modi di fattorizzare un numero: scorre i divisori, per ciascuno calcola ricorsivamente tutti i sotto-divisiri, poi usa lo spread per ricomperre l'array. Questa genera molti duplicati (stessi fattori ma in ordine diverso)
function strafatto0(n) {
var r = []
var ds = divisors(n)
if (ds.length >= 1) {
for (var i = 0; i < ds.length; i++) {
var h = ds[i]
var ts = strafatto0(n / h)
for (var j = 0; j < ts.length; j++)
r.push([h, ...ts[j]])
}
} else {
r.push(n > 1 ? [n] : [])
}
return r
}
// chiama strafatto0, poi porta tutte le fattorizzazioni in forma canonica e ordina l'array (come in ordnung); alla fine, la filter toglie tutti i duplicati, nonché il primo elemento (che sarà sempre [n] stesso)
function strafatto(n) {
function acmp(aa, bb) {
if (aa.length != bb.length)
return aa.length - bb.length
else
for (var h = 0; h < aa.length; h++)
if (aa[h] != bb[h]) return aa[h] - bb[h]
return 0
}
var fs = strafatto0(n)
for (d of fs) {
d.sort((a, b) => a - b)
}
return fs.sort(acmp).filter((v, i, a) => (i > 0) ? acmp(a[i], a[i - 1]) != 0 : false)
}
// Esercizio strafatto, seconda versione: con calcolo divisori, generazione fattori e pulizia (fatta evitando di inserire) in un solo passaggio
function strafatto2(n) {
var r = []
for (var d = 2; d <= Math.sqrt(n); d++)
if (n % d == 0) {
for (var x of [[n / d], ...strafatto2(n / d)])
if (d <= x[0])
r.push([d, ...x])
}
return r
}
Calc1
Si scriva una funzione calc1(a) che, dato come argomento un array a, in cui il primo elemento è un operatore aritmetico (+, -, *, /) rappresentato come carattere, restituisca il risultato ottenuto applicando l’operatore fra tutti i rimanente elementi dell’array. Suggerimenti: splendida occasione per un assegnamento destrutturante Esempi:
calc1(["+",4,5,2]) → 11
calc1(["*",4,5,2]) → 40
calc1(["/",64,2,2,4]) → 4
// Esercizio calc1. Uso un assegnamento destrutturante negli argomenti formali (incluso lo spread con rest) per estrarre direttamente le variabili dall'argomento attuale, poi uso una reduce sugli argomenti rest
function calc1([op, p, ...args]) {
switch (op) {
case "+": return args.reduce((a, v) => a + v, p)
case "+": return args.reduce((a, v) => a - v, p)
case "*": return args.reduce((a, v) => a * v, p)
case "/": return args.reduce((a, v) => a / v, p)
}
}
CalcT
Si scriva una funzione calcT(a) che, dato come argomento un array a, in cui il primo elemento è un operatore aritmetico (+, -, *, /) rappresentato come carattere, restituisca il risultato ottenuto applicando l’operatore fra tutti i rimanente elementi dell’array, con la cautela che se uno degli altri elementi è esso stesso un array, viene prima valutato il suo valore secondo la stessa regola. Esempi:
calcT(["+",4,["*",3,2]]) → 10
calcT(["*",4,5,2]) → 40
calcT(["/",64,["*",["+",2,2],4]]) → 4
function calcT(x) {
if (x instanceof Array) {
[op, p, ...args] = x
switch (op) {
case "+": return args.reduce((a, v) => a + calcT(v), calcT(p))
case "+": return args.reduce((a, v) => a - calcT(v), calcT(p))
case "*": return args.reduce((a, v) => a * calcT(v), calcT(p))
case "/": return args.reduce((a, v) => a / calcT(v), calcT(p))
}
} else
return x
}
La misteriosa funzione r
Si consideri la funzione r così definita:
function r([a,...b]) { return a?[...r(b),a]:[] }
Senza eseguire il codice, siete in grado di capire cosa fa e come funziona? Provate a scriverne una versione più chiara. Dopo aver svolto questo esercizio, provate a riscrivere calcT (dall’esercizio precedente) nello stile della misteriosa funzione r.
// la funzione r effettua ricorsivamente l'inversione di un array.
// se il primo elemento di a è considerato 'falsy' allora restituisce array vuoto []
function r_chiaro([a, ...b]) {
if (a == undefined)
return [];
return [...r_chiaro(b), a];
}
Titolo
Scrivete un frammento di codice JavaScript che, una volta eseguito, aggiunga a tutte le stringhe del vostro programma un metodo titolo() il cui effetto è di restituire la stringa su cui è invocato, ma tutta in maiuscolo e con uno spazio aggiunto fra le lettere della stringa originale. Esempi (dopo aver aggiunto il metodo):
“pippo”.titolo() → “P I P P O”
`3 * 2 fa ${3 * 2}`.titolo() → “3 * 2 F A 6”
var s=“Pluto”; s.titolo() → “P L U T O”
String.prototype.titolo = function() {
return [...this].join(' ').toUpperCase()
}
Math.rational
Aggiungete alla classe Math un metodo rational(x) che, ricevuto come argomento un numero x (non necessariamente un intero!), restituisca un array con due elementi interi che diano, rispettivamente, il numeratore e il denominatore di una rappresentazione razionale (semplificata) di x. Esempi (dopo aver aggiunto il metodo):
Math.rational(4.5) → [9,2]
Math.rational(-3) → [-3,1]
Math.rational(3.1415) → [6283,2000]
Math.rational = function(x) {
// Utilità -> https://stackoverflow.com/questions/1458633/how-to-deal-with-floating-point-number-precision-in-javascript
function strip(number) {
return (parseFloat(number).toPrecision(12));
}
const mcd = function (a,b) {
let c;
a = Math.abs(a); b = Math.abs(b)
while (b) { c = a%b; a = b; b = c; }
return a
}
// Utilità
const simplify = function(num,den) {
let m = mcd(num,den)
let s = den < 0 ? -1 : 1 // segno
num /= m*s
den /= m*s
return [num,den];
}
let zeroCounter = 0;
// portare x in intero
while (x % 1 != 0) {
x = strip(x*10);
zeroCounter++;
}
let [n,d] = simplify(x,Number("1"+"0".repeat(zeroCounter)));
return [n,d];
}
Esprit de géométrie
Si scriva una classe PuntoCartesiano che rappresenti, prevedibilmente, un punto geometrico in coordinate cartesiane (x,y). Oltre a un costruttore, serviranno i metodi seguenti:
p.dist(q) - restituisce la distanza euclidea fra i punti p e q
p.translate(q) - sposta il punto p, traslandolo di q
p.zero() - sposta il punto p alle coordinate (0,0)
Potete, a piacere, aggiungere altri metodi che vi sembrino interessanti.
class PuntoCartesiano {
constructor(x,y) {
this.x = x;
this.y = y;
}
zero() {
this.x = 0;
this.y = 0;
}
translate(q) {
this.x += q.x;
this.y += q.y;
}
dist(q) {
return Math.sqrt((q.x - this.x)**2 + (q.x - this.x)**2);
}
}
Studente
Si scriva una classe Studente, destinata a rappresentare uno studente. Ogni studente ha obbligatoriamente un numero di matricola (intero) e un nome (stringa); può avere opzionalmente altri attributi a vostro piacere: per esempio, corso di laurea, anno, corso (A/B/C), nazionalità, ecc. Ogni studente ha anche una carriera, inizialmente vuota; la carriera è una lista di esami superati, ciascuno dei quali rappresentato da un oggetto con campi {materia: stringa, cfu: numero, voto: numero, lode: booleano}. La classe dovrà implementare almeno:
- un costruttore che consenta di creare nuovi studenti, specificando tutti gli attributi obbligatori, e di volta in volta alcuni (anche nessuno o tutti) degli attributi opzionali, in qualunque combinazione;
- un metodo passato(esame) che aggiunge l’esame alla carriera dello studente
- un metodo media() che restituisce il calcolo della media (pesata per cfu, con 30 e lode che conta come 32)
// in qualunque combinazione == destrutturazione
class Studente {
constructor({nome = nome, matricola = matricola, opzionali : {corso_laurea = '', anno = 2021, corso = '', nazionalità = ''} = {}}) {
this.nome = nome;
this.matricola = matricola;
this.carriera = [];
this.opzionali = {corso_laurea, anno, corso, nazionalità};
}
passato(esame) {
this.carriera.push(esame);
}
media() {
/* Media = ∑ (voto * CFU esame) / ∑ (CFU esame) */
let pesi = 0 , cfu_tot = 0;
for (var obj of this.carriera) {
if (obj.lode)
pesi += 32 * obj.cfu;
else
pesi += obj.voto * obj.cfu;
cfu_tot += obj.cfu;
}
return pesi/cfu_tot;
}
}