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;
  }
}