Kurzusok
/
JavaScript
JavaScript
/
Feladatok
/
Koordináta-transzformáció
Koordináta-transzformáció

Koordináta-transzformáció

Tanulófeladat

Bevezetés

A closure egy programozási minta a JavaScriptben, amely lehetővé teszi, hogy egy külső lexical scope változóit egy egymásba ágyazott kódblokkon belül használd. A JavaScript átlátszóan kezeli a closure-öket, és gyakran anélkül használjuk őket, hogy tudnánk, mik is azok.

// 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

A kapcsos zárójeleken ({}) kívül a függvények (és az osztályok) is új scope-okat hoznak létre, és képesek bezárni értékeket:

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

Ahogy a twoDozen példák mutatják, az értékeket be lehet zárni egy egymásba ágyazott scope-ba (függvény, blokk stb.), de nem lehet kihúzni őket abból a kontextusból. A legtöbb esetben a modern JavaScriptben szándékos, hogy egy érték ne szivárogjon ki egy külső scope-ba.

Closure-ök állapot megőrzésére és értékek továbbadására

Egy módosítható változódeklaráció (például let vagy var) használatával megőrizhető valamennyi állapot:

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

Értékek bezárása az állapot kiszivárgása nélkül

A két ötletet ötvözve: ha egy értéket bezárunk, hogy megőrizzük az állapotot, és a bezárt értékek nem szivárognak ki, akkor privát értékeket hozhatunk létre.

A leggyakoribb módszer, hogy készítünk egy függvényt, amely egy olyan függvényt ad vissza, ami bezár magába valamennyi állapotot.

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

Sok programozó nehéz fogalomnak tartja a closure-öket, és az, hogy egy függvény függvényt adjon vissza, nem gyakori, sőt néhány programozási nyelvben egyenesen lehetetlen. Ha több olvasnivalót szeretnél, az MDN closure-ökről szóló útmutatója elég átfogó.

Utasítások

A tervezőcég, ahol dolgozol, eddig elsősorban CSS-transzformációkkal épített weboldalakat. Némi megbeszélés után úgy döntesz, hogy elkezded használni a JavaScriptet, hogy bizonyos számításokat dinamikusan végezz el. A csapattársaid közül néhányan kevésbé jártasak JavaScriptben, ezért úgy döntesz, hogy closure-t használsz, hogy újrahasznosítható transzformációt készíts {x, y} koordinátapárokhoz.

1. A koordináták eltolása

Valósítsd meg a translate2d függvényt, amely closure-t használva visszaad egy függvényt, ami egy koordinátapár ismételhető 2D eltolását végzi.

A geometriában az eltolás azt jelenti, hogy pontokat, vektorokat vagy alakzatokat ugyanakkora távolsággal mozgatunk el egy adott irányba. Úgy is értelmezhető, mint egy konstans hozzáadása minden ponthoz.

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

2. A koordináták skálázása

Valósítsd meg a scale2d függvényt, amely closure-t használva visszaad egy függvényt, ami egy koordinátapár ismételhető 2D skálázását végzi.

A geometriában az egyenletes skálázás vektorok vagy alakzatok ugyanabba az irányba történő nagyítását vagy kicsinyítését jelenti. Úgy is értelmezhető, mint minden pont megszorzása egy konstanssal (skálázási tényezővel).

Ehhez a feladathoz feltételezd, hogy csak pozitív skálázási értékek vannak.

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

3. Transzformációs függvények összeállítása

Kombinálj két transzformációs függvényt, hogy ismételhető transzformációt hozz létre. Ezt gyakran függvénykompozíciónak nevezik, ahol az első függvény f(x) eredménye lesz a második függvény g(x) bemenete.

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

4. A függvények eredményeinek mentése

Valósítsd meg a memoizeTransform függvényt. Ez egy függvényt vesz át memoizálásra, majd visszaad egy új függvényt, amely megjegyzi a kapott függvény bemeneteit, így az utolsó visszatérési érték „megjegyezhető”, és csak egyszer számítódik ki, ha ugyanazokkal az argumentumokkal hívják meg újra.

A memoizálást néha a dinamikus programozásban használják. Lehetővé teszi, hogy a költséges műveleteket csak egyszer hajtsák végre, mivel az eredményeiket megjegyzik. Megjegyzés: ebben a feladatban csak az utolsó eredményt jegyzi meg, ellentétben néhány dinamikus programozási megoldással, amelyek minden eredményt memoizálnak.

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
Szerkesztés GitHubon A hivatkozás új ablakban vagy lapon nyílik meg
JavaScript Exercism

Készen állsz elkezdeni a(z) Koordináta-transzformáció feladatot?

Iratkozz fel az Exercism-re, hogy megtanuld és elsajátítsd a(z) JavaScript nyelvet 37 fogalom159 feladat segítségével, valódi emberi mentorálással, mindez ingyen.