11 minutes
Esercizi liberi lezioni 1-14
Per completezza, si rimanda al primo post del sito per ulteriori informazioni.
Si ricorda che dello stesso esercizio possono esistere più varianti.
Produttoria
Si scriva una funzione produttoria(a,b) che, dati come argomenti due interi positivi a e b, con a ≤ b, restituisca il prodotto di tutti gli interi fra a e b, estremi compresi. Esempi:
produttoria(4,6) → 120
produttoria(10,10) → 10
produttoria(10,11) → 110
function produttoria(a,b) {
let n = 1;
for (let i = a; i <= b; i++) {
n *= i;
}
return n;
}
Quaterne
Si scriva una funzione quaterne(a,b) che, dati come argomenti due interi a e b, con a ≤ b, restituisca il numero di quaterne (cioè sequenze distinte di quattro numeri consecutivi) comprese fra a e b, estremi esclusi. Esempi:
quaterne(4,6) → 0
quaterne(4,10) → 2 (sono: 5/6/7/8 e 6/7/8/9)
quaterne(-4,10) → 10 (sono -3/-2/-1/0, -2/-1/0/1, … , 5/6/7/8, 6/7/8/9)
function quaterne(a,b) {
if (a == b || a > b)
return 0;
let n_quaterne = 0;
for (let i = a; i < b; i++) {
if ((i + 4) < b)
n_quaterne++;
}
return n_quaterne;
}
Numeri perfetti
Un numero naturale n si dice perfetto se è uguale alla somma dei propri divisori propri (ovvero, tutti i suoi divisori positivi escluso n). Si scriva una funzione perfetto(n) che, dato un numero naturale n, restituisca true se n è perfetto, false altrimenti. Esempi:
perfetto(6) → true (infatti 1+2+3 = 6)
perfetto(10) → false (infatti 1+2+5 ≠ 10)
perfetto(28) → true (infatti 1+2+4+7+14 = 28)
function perfetto(n) {
let divisori = [];
let tot = 0;
for (let i = 1; i < n; i++) {
if (n % i == 0)
divisori.push(i);
}
for (let i in divisori)
tot += divisori[i];
return tot == n;
}
Range
Si scriva una funzione range(a,b) che, dati due interi a e b restituisca un array ordinato di interi, contenente tutti e soli gli interi i tali che a ≤ i e i ≤ b. Esempi:
range(2,6) → [2, 3, 4, 5, 6]
range(10,10) → [10]
range(-5, 1) → [-5, -4, -3, -2, -1, 0, 1]
range(10, 4) → []
function range(a,b) {
let arr = [];
for (let i = a; i <= b; i++) {
arr.push(i);
}
return arr;
}
Penultimo
Si scriva una funzione penultimo(a) che, dato un array di stringhe a, restituisca la penultima stringa secondo l’ordine alfabetico fra quelle presenti nell’array, oppure undefined se non esiste una penultima. Esempi:
penultimo([“pera”, “zucca”, “mela”]) → “pera”
penultimo([“dattero”, “zucca”, “mela”]) → “mela”
penultimo([“zucca”]) → undefined
function penultimo(a) {
if (a.length < 2)
return undefined;
a.sort();
return a[a.length - 2];
}
Ordinamento per lunghezza
Si scriva una funzione ordlun(a) che, dato un array di stringhe a, restituisca un array contenente le stesse stringhe, ordinate secondo la loro lunghezza (dalla più breve alla più lunga); a parità di lunghezza, andranno ordinate secondo l’ordine alfabetico. Esempi:
ordlun([“pera”, “zucca”, “mela”]) → [“mela”, “pera”, “zucca”]
ordlun([“dattero”, “zucca”, “mela”]) → [“mela”, “zucca”, “dattero”]
ordlun([]) → []
function ordlun(a) {
function compare(str1,str2) {
let len1 = str1.length;
let len2 = str2.length;
if (len1 < len2)
return -1;
else if (len1 > len2)
return 1;
else {
// stessa lunghezza delle 2 stringhe => ordinamento lessicografico
if (str1 < str2)
return -1;
return 1;
}
}
return a.sort(compare);
}
Multiinsiemi - conversione
Un multiinsieme è una generalizzazione del concetto di insieme in cui lo stesso elemento può apparire più volte. Si potrebbe rappresentare un multiinsieme come un array, per esempio: [4, 7, 10, 4, 9, 7, 4]. Lo si potrebbe anche rappresentare come un oggetto, in cui gli elementi sono le chiavi, e i corrispondenti valori indicano quante volte compare quell’elemento. L’array precedente può dunque essere rappresentato come { 4: 3, 7: 2, 9: 1, 10: 1}.
Si scriva una funzione cvtmi(a) che, data la rappresentazione ad array di un multiinsieme (di interi o stringhe), restituisca la corrispondente rappresentazione a oggetto. Esempi:
cvtmi([“pera”, “zucca”, “mela”]) → { mela: 1, pera: 1, zucca: 1}
cvtmi([“pera”, “pera”, “pera”, “zucca”]) → {pera: 3, zucca: 1}
cvtmi([1,2,3,4,3,4,5,2,1,1,9]) → {1:3, 2:2, 3:2, 4:2, 5:1, 9:1}
function cvtmi(a) {
let obj = {};
for (let i in a) {
if (a[i] in obj)
obj[a[i]] = obj[a[i]] + 1;
else
obj[a[i]] = 1;
}
return obj;
}
Multiinsiemi - unione e intersezione
Si scrivano due funzioni: unionemi(a,b) che, dati due oggetti a e b che rappresentano multiinsiemi, come definiti nell’esercizio precedente, restituisca un oggetto che rappresenta l’unione dei due multiinsiemi, e intersezionemi(a,b) che allo stesso modo restituisce l’intersezione fra i due multiinsiemi. Esempi:
unionemi({1:4, 2:1},{1:3, 3:1}) → {1:7, 2:1, 3:1}
intersezionemi({1:4, 2:1},{1:3, 3:1}) → {1:3}
intersezionemi({1:4, 2:1},{}) → {}
function unionemi(a,b) {
let obj = {};
for (var e in a)
obj[e] = a[e];
for (var e in b) {
if (e in obj)
obj[e] = obj[e] + b[e];
else
obj[e] = b[e];
}
return obj;
}
function intersezionemi(a,b) {
let obj = {};
for (var e in a) {
if (e in b)
obj[e] = (a[e] > b[e] ? b[e] : a[e]);
}
return obj;
}
Conta vocali
Si scriva una funzione contaVocali(s) che, data una stringa s, restituisca il numero totale di vocali (lettere a, e, i, o, u, sia maiuscole che minuscole) presenti in s. Esempi:
contaVocali(“Ai lati d’Italia”) → 8
contaVocali(“qwerty”) → 1
contaVocali(“3463234”) → 0
contaVocali(“Nel mezzo del cammin di nostra vita”) → 11
function contaVocali(s) {
let count = 0;
let vocali = ['a','e','i','o','u'];
for (let char of s) {
if (vocali.includes(char) || vocali.includes(char.toLowerCase()))
count++;
}
return count;
}
Una firma atipica
Si scriva una funzione firma(s) che, data una stringa s, restituisca un intero positivo k calcolato come segue: si immagini di sostituire ogni vocale (maiuscola o minuscola) o spazio in s con 1, e qualunque altro carattere con 0. Si consideri poi la stringa risultante come un numero binario, e sia k il suo valore. Esempi:
firma(“Vincenzo Gervasi”) → 18853
firma(“Alina Sirbu”) → 1385
firma("") → 0
function firma(s) {
let bin = "";
let vocali = ['a','e','i','o','u',' '];
let k = 0; // risultato finale
for (let char of s) {
if (vocali.includes(char) || vocali.includes(char.toLowerCase())) {
bin += "1";
} else {
bin += "0";
}
}
// conversione binario-decimale
let exp = 0;
for (let i = bin.length - 1; i >= 0; i--) {
k += (bin[i] == 1 ? 1 : 0) * (2**exp);
exp++;
}
return k;
}
Applicare una funzione a un oggetto
Si scriva una funzione applyobj(o, f) che, dato un oggetto o e una funzione f, restituisca un oggetto o’ con le stesse chiavi di o, e in cui il valore di ogni chiave k sia dato dall’applicazione di f al valore della chiave k in o, ovvero: o’.k == f(o.k). Esempi:
applyobj({pere: 3, mele: 1}, x=>2*x) → {pere: 6, mele: 2}
applyobj({io: “Vincenzo”, tu: “Alina”}, e=>e.length) → {io: 8, tu: 5}
applyobj({io: 8, tu: 5}, e=>e) → {io: 8, tu: 5}
applyobj({}, e=>e+1) → {}
function applyobj(o,f) {
for (let key in o) {
o[key] = f(o[key])
}
return o;
}
Maxprod
Si scriva una funzione maxprod(a) che, dato un array di numeri naturali a, restituisca un oggetto con struttura {idx: i, val: n} in cui i sia l’indice e n il valore dell’elemento in a per cui è massimo il prodotto dell’indice per il valore dell’elemento. In caso di parità, si scelga l’elemento di indice minore. Esempi:
maxprod([8, 2, 2, 1]) → {idx: 2, val: 2}
maxprod([1, 8, 1, 2, 2]) → {idx: 1, val: 8}
function maxprod(a) {
let obj = {idx: 0, val: 0}
let val = 0;
let idx = 0;
let val_max_prod = -Infinity;
for (let i = 0; i < a.length; i++) {
if (a[i]*i > val_max_prod) {
val_max_prod = a[i]*i;
val = a[i]
idx = i;
}
}
obj["idx"] = idx;
obj["val"] = val;
return obj;
}
Appiattimento
Si consideri un array i cui elementi possono essere o numeri, oppure altri array dello stesso tipo (ovvero, aventi per elementi o numeri, oppure altri array dello stesso tipo, e così via). Si scriva una funzione appiattisci(a) che, dato un array a come descritto sopra, restituisca un array contenente i soli numeri, nello stesso ordine in cui comparivano nell’array a. Esempi:
appiattisci([8, [2, 2], 1]) → [8, 2, 2, 1]
appiattisci([[1], 8, [1, 2], 2, []]) → [1, 8, 1, 2, 2]
function appiattisci(a) {
let res = [];
function rec_flat(a) {
for (let elem of a) {
if (elem instanceof Array) {
rec_flat(elem);
} else if (typeof elem == 'number') {
res.push(elem);
}
}
}
for (let i = 0; i < a.length; i++) {
if (a[i] instanceof Array)
rec_flat(a[i]);
else if (typeof a[i] == 'number')
res.push(a[i]);
}
return res;
}
Rosa dei venti
Si considerino le quattro direzioni cardinali (nord, est, sud, ovest), ciascuna codificata con la lettera corrispondente in (N, E, S, W), nonché le direzioni intermedie codificate con due lettere in ordine qualsiasi (es: NE = EN = nord-est). Si scriva una funzione rosa(s) che, data una stringa s contenente la codifica di una direzione come indicato sopra, restituisca un oggetto {x: i, y: j} in cui i e j sono valori fra -1, 0 e 1 che rappresentano lo spostamento unitario lungo l’asse x e lungo l’asse y, rispettivamente, corrispondente alla direzione codificata da s. Esempi:
rosa(“NE”) → {x: 1, y: 1}
rosa(“EN”) → {x: 1, y: 1}
rosa(“S”) → {x: 0, y: -1}
rosa(“NW”) → {x: -1, y: 1}
function rosa(s) {
let obj = {x: 0, y: 0}
let arr = s.split('');
for (let i in arr) {
switch(arr[i]) {
case "N": obj["y"] = 1;
break;
case "S": obj["y"] = -1;
break;
case "W": obj["x"] = -1;
break;
case "E": obj["x"] = 1;
break;
}
}
return obj;
}
Percorso
Si scriva una funzione percorso(a) che, dato un array a di direzioni codificate da stringhe come nell’esercizio precedente, restituisca la distanza euclidea fra l’origine degli assi e la posizione finale di un ipotetico viaggiatore che parta dall’origine e faccia un passo nella direzione indicata da ogni elemento del percorso, in ordine. Esempi:
percorso([]) → 0
percorso([“N”,“E”]) → 1.4142136
percorso([“N”]) → 1
percorso([“N”,“N”,“NE”]) → 3.1622777
percorso([“N”,“S”]) → 0
percorso([“N”,“S”,“NE”]) → 1.4142136
function percorso(a) {
if (a.length < 1)
return 0;
let obj = {x: 0, y: 0};
let tmp;
for (let i = 0; i < a.length; i++) {
tmp = rosa(a[i]);
obj["x"] += tmp["x"];
obj["y"] += tmp["y"];
}
return Math.sqrt((obj["x"])**2 + (obj["y"])**2);
}
Composizione di funzioni
Si scriva una funzione componi(f,g) che, date due funzioni f e g, restituisca una funzione h tale che h(x) = g(f(x)). Esempi:
componi(x=>2x, x=>2x)(3) → 12
componi(s=>s.length, x=>x**2+1)(“Vincenzo”) → 65
componi(a=>a[0], s=>s.length)([“pere”,“banane”]) → 4
function componi(f,g) {
return function h(x) {
return g(f(x));
}
}
// oppure
function componi(f,g) {
return (h = x => f(g(x)));
}
Differenza di date
Si rappresenti una data come un oggetto della forma {giorno: g, mese: m, anno: a}, in cui g, m, e a sono indicati come numeri (con l’usuale convenzione: Gennaio = 1, … Dicembre = 12 per i mesi). Si scriva una funzione diff(d1, d2) che, date due date nel formato indicato sopra, restituisca il numero di giorni trascorsi fra la prima e la seconda data (si usino i numeri negativi se d2 è precedente a d1). Si ricorda che Febbraio è lungo 28 giorni negli anni ordinari, e 29 giorni negli anni bisestili; sono bisestili tutti gli anni divisibili per 4, salvo quelli che sono divisibili per 100 (che invece sono ordinari). Ai fini dell’esercizio si possono trascurare le varie riforme del calendario avvenute nei secoli. Esempi:
diff({giorno: 1, mese: 1, anno: 2020},{giorno: 4, mese: 2, anno: 2020}) → 34
diff({giorno: 1, mese: 1, anno: 2019},{giorno: 1, mese: 1, anno: 2020}) → 365
function diff(d1,d2) {
let tmp_d1 = new Date(d1["anno"],d1["mese"] - 1,d1["giorno"]);
let tmp_d2 = new Date(d2["anno"],d2["mese"] - 1,d2["giorno"]);
let res = tmp_d2 - tmp_d1;
return Math.ceil(res/(24*3600*1000)); // divido per i millisecondi in un giorno
}
Dividere l’eredità
Si consideri un albero k-ario, in cui i nodi hanno la struttura {val: n, figli: [ t1, … tk ]}, come visto a lezione. Si vuole distribuire l’eredità di ogni nodo intermedio ai suoi figli in questo modo: il valore n di un nodo viene distribuito in parti uguali ai figli, ciascuno dei quali riceve dunque n/k. La quota ereditata viene sommata al valore di n di ciascun erede, e se l’erede non è una foglia, il risultato viene ulteriormente diviso ai figli, e così via. Si scriva una funzione eredita(t) che, ricevuto come argomento un albero t nel formato descritto sopra, restituisca il valore totale (n proprio più quota ereditata) del nodo foglia con valore massimo. Esempi:
t={val: 16, figli: [{val: 4},{val: 2, figli: [{val: 8},{val: 2}]}]}
eredita(t) → 13
eredita({val: 5}) → 5
function eredita(t) {
if (t === undefined)
return -Infinity;
if (t.figli === undefined)
return t.val;
t.val = t.val/t.figli.length;
let max = t.val;
for (let figlio of t.figli) {
figlio.val += t.val;
if (figlio.val > max)
max = figlio.val;
if (figlio.figli) {
let max_of_children = eredita(figlio);
if (max_of_children > max)
max = max_of_children
}
}
return max;
}