Track
/
JavaScript
JavaScript
/
Esercizi
/
Trasformazione di coordinate
Trasformazione di coordinate

Trasformazione di coordinate

Esercizio di apprendimento

Introduzione

Le closure sono un pattern di programmazione in JavaScript che permette di usare variabili di uno scope lessicale esterno all'interno di un blocco di codice annidato. JavaScript supporta le closure in modo trasparente, e spesso vengono usate senza sapere cosa siano.

// Top-level declarations are global-scope
const dozen = 12;

{
  // Braces create a new block-scope
  // Referencing the outer variable is a closure.
  const twoDozen = dozen * 2;
}

// Because of the block-scope declaration, twoDozen is not available here.
twoDozen;
// => Uncaught ReferenceError: twoDozen is not defined

Oltre alle parentesi graffe ({}), anche le funzioni (e le classi) creano nuovi scope e possono racchiudere valori:

const dozen = 12;

// Functions create a new function-scope and block-scope.
// Referencing the outer variable here is a closure.
function nDozen(n) {
  // This is declared inside the function scope, and uses the top-level scope.
  // This works, and encloses the value 12.
  const twoDozen = dozen * 2;

  // This only uses the locally declared variable and the passed argument to the parameter `n`
  return (twoDozen / 2) * n;
}

// Because of the function-scope declaration, twoDozen is not available here.
twoDozen;
// => Uncaught ReferenceError: twoDozen is not defined

Come mostrano gli esempi con twoDozen, i valori possono essere racchiusi in uno scope annidato (funzione, blocco, ecc.), ma non possono essere estratti da quel contesto. Nella maggior parte dei casi, nel JavaScript moderno si vuole che un valore non fuoriesca verso uno scope esterno.

Le closure per conservare lo stato e trasmettere valori

Usare una dichiarazione di variabile mutabile (come let o var) permette di conservare uno stato:

let counter = 0;

// This function closure increments the counter's state in the outer lexical context.
// This way the counter can be shared between many calling contexts.
export function increment() {
  counter += 1;
  return counter;
}

increment();
// => 1

counter;
// => 1

Racchiudere valori senza far fuoriuscire lo stato

Combinando le due idee, racchiudere un valore per conservarne lo stato e il fatto che i valori racchiusi non fuoriescano all'esterno, è possibile creare valori privati.

Il metodo più comune è creare una funzione che restituisce una funzione che racchiude un certo stato.

export function makeCounter() {
  let counter = 0;

  // This returns a new function that encloses the local variable counter
  return function increment() {
    counter += 1;
    return counter;
  };
}

// Counter did not leak
counter;
// => Uncaught ReferenceError: counter is not defined

// This creates a new counter.
// This assigns the increment function to the variable myFirstCounter.
const myFirstCounter = makeCounter();

typeof myFirstCounter;
// => function

myFirstCounter.name;
// => increment

myFirstCounter();
// => 1
myFirstCounter();
// => 2

// This creates new counter (with new, separate local state / enclosed counter variable)
const mySecondCounter = makeCounter();

mySecondCounter();
// => 1

// It does not affect the first counter.

myFirstCounter();
// => 3
Note

Molti programmatori trovano le closure un concetto difficile, e restituire una funzione da una funzione non è comune, né addirittura possibile in tutti i linguaggi di programmazione. Se vuoi più materiale da leggere, la guida su MDN sulle closure è piuttosto completa.

Istruzioni

La tua azienda di design ha lavorato principalmente con le trasformazioni CSS per costruire pagine web. Dopo qualche discussione, si decide di iniziare a usare JavaScript per eseguire alcuni calcoli in modo dinamico. Alcuni dei tuoi colleghi hanno meno esperienza con JavaScript, così decidi di usare una closure per creare una trasformazione riutilizzabile per coppie di coordinate {x, y}.

1. Trasla le coordinate

Implementa la funzione translate2d che restituisce una funzione che, tramite una closure, esegue una traslazione bidimensionale ripetibile di una coppia di coordinate.

In geometria, la traslazione indica lo spostamento di punti, vettori o forme della stessa distanza in una sola direzione. Può essere interpretata come la somma di una costante a ogni punto.

const moveCoordinatesRight2Px = translate2d(2, 0);
const result = moveCoordinatesRight2Px(4, 8);
// result => [6, 8]

2. Scala le coordinate

Implementa la funzione scale2d che restituisce una funzione che, tramite una closure, esegue un ridimensionamento bidimensionale ripetibile di una coppia di coordinate.

In geometria, il ridimensionamento uniforme indica l'ingrandimento o la riduzione di vettori o forme nella stessa direzione. Può essere interpretato come la moltiplicazione di ogni punto per una costante (fattore di scala).

Per questo esercizio, supponi che i valori di scala siano solo positivi.

const doubleScale = scale2d(2, 2);
const result = doubleScale(6, -3);
// result => [12, -6]

3. Componi le funzioni di trasformazione

Combina due funzioni di trasformazione per eseguire una trasformazione ripetibile. Questa operazione è spesso chiamata composizione di funzioni: il risultato della prima funzione, 'f(x)', viene usato come input della seconda, 'g(x)'.

const moveCoordinatesRight2Px = translate2d(2, 0);
const doubleCoordinates = scale2d(2, 2);
const composedTransformations = composeTransform(
  moveCoordinatesRight2Px,
  doubleCoordinates,
);
const result = composedTransformations(0, 1);
// result => [4, 2]

4. Salva i risultati delle funzioni

Implementa la funzione memoizeTransform. Riceve una funzione da memoizzare e restituisce una nuova funzione che ricorda gli input della funzione fornita, così che l'ultimo valore restituito possa essere «ricordato» e calcolato una sola volta se la funzione viene chiamata di nuovo con gli stessi argomenti.

La memoizzazione è talvolta usata nella programmazione dinamica. Permette di eseguire operazioni costose una sola volta, dato che i loro risultati vengono ricordati. Nota: in questo esercizio viene ricordato solo l'ultimo risultato, a differenza di alcune soluzioni di programmazione dinamica che memoizzano tutti i risultati.

const tripleScale = scale2d(3, 3);
const memoizedScale = memoizeTransform(tripleScale);

memoizedScale(4, 3); // => [12, 9], this is computed since it hasn't been computed before for the arguments
memoizedScale(4, 3); // => [12, 9], this is remembered, since it was computed already
Modifica tramite GitHub Il link si apre in una nuova finestra o scheda
JavaScript Exercism

Vuoi iniziare Trasformazione di coordinate?

Iscriviti a Exercism per imparare e padroneggiare JavaScript con 37 concetti159 esercizi e il mentoring di persone reali, tutto gratis.