Διαδρομές
/
JavaScript
JavaScript
/
Ασκήσεις
/
Μετασχηματισμός συντεταγμένων
Μετασχηματισμός συντεταγμένων

Μετασχηματισμός συντεταγμένων

Άσκηση εκμάθησης

Εισαγωγή

Τα closures είναι ένα μοτίβο προγραμματισμού στη JavaScript που επιτρέπει να χρησιμοποιούνται μεταβλητές από μια εξωτερική λεξική εμβέλεια μέσα σε ένα εμφωλευμένο μπλοκ κώδικα. Η JavaScript υποστηρίζει τα closures με διαφάνεια, και συχνά χρησιμοποιούνται χωρίς να ξέρει κανείς τι είναι.

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

Εκτός από τα άγκιστρα {}, οι συναρτήσεις (και οι κλάσεις) δημιουργούν επίσης νέες εμβέλειες, και μπορούν να περικλείσουν τιμές:

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

Όπως δείχνουν τα παραδείγματα με το twoDozen, οι τιμές μπορούν να περικλειστούν σε μια εμφωλευμένη εμβέλεια (συνάρτηση, μπλοκ κ.λπ.), αλλά δεν μπορούν να τραβηχτούν έξω από αυτό το πλαίσιο. Στις περισσότερες περιπτώσεις, στη σύγχρονη JavaScript σκοπός είναι μια τιμή να μη διαρρέει σε εξωτερική εμβέλεια.

Closures για αποθήκευση κατάστασης και μεταβίβαση τιμών

Η χρήση μιας δήλωσης μεταβλητής που μπορεί να τροποποιηθεί (όπως let ή var) επιτρέπει τη διατήρηση κάποιας κατάστασης:

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

Περικλείοντας τιμές χωρίς διαρροή κατάστασης

Συνδυάζοντας τις δύο ιδέες, το να περικλείεις μια τιμή για να διατηρήσεις κατάσταση και το ότι οι περικλεισμένες τιμές δεν διαρρέουν προς τα έξω, είναι δυνατό να δημιουργήσεις ιδιωτικές τιμές.

Η πιο συνηθισμένη μέθοδος είναι να φτιάξεις μια συνάρτηση που επιστρέφει μια συνάρτηση η οποία περικλείει κάποια κατάσταση.

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

Πολλοί προγραμματιστές βρίσκουν τα closures δύσκολη έννοια, και η επιστροφή μιας συνάρτησης από μια συνάρτηση δεν είναι συνηθισμένη ή ακόμα και εφικτή σε όλες τις γλώσσες προγραμματισμού. Αν θέλεις περισσότερο υλικό για διάβασμα, ο οδηγός του MDN για τα Closures είναι αρκετά περιεκτικός.

Οδηγίες

Η εταιρεία σχεδιασμού σου έχει δουλέψει κυρίως με μετασχηματισμούς CSS για να φτιάχνει ιστοσελίδες. Μετά από κάποια συζήτηση, παίρνετε την απόφαση να αρχίσετε να χρησιμοποιείτε JavaScript για να εκτελείτε κάποιους υπολογισμούς δυναμικά. Κάποιοι από τους συναδέλφους σου έχουν λιγότερη εμπειρία με τη JavaScript, οπότε αποφασίζεις να χρησιμοποιήσεις ένα closure για να δημιουργήσεις έναν επαναχρησιμοποιήσιμο μετασχηματισμό για ζεύγη συντεταγμένων {x, y}.

1. Μετατόπισε τις συντεταγμένες

Υλοποίησε τη συνάρτηση translate2d που επιστρέφει μια συνάρτηση η οποία χρησιμοποιεί ένα closure για να εκτελεί επαναλαμβανόμενη δισδιάστατη μετατόπιση ενός ζεύγους συντεταγμένων.

Στη γεωμετρία, η μετατόπιση αναφέρεται στη μετακίνηση σημείων, διανυσμάτων ή σχημάτων κατά την ίδια απόσταση προς μία κατεύθυνση. Μπορεί να ερμηνευτεί ως πρόσθεση μιας σταθεράς σε κάθε σημείο.

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

2. Κλιμάκωσε τις συντεταγμένες

Υλοποίησε τη συνάρτηση scale2d που επιστρέφει μια συνάρτηση η οποία χρησιμοποιεί ένα closure για να εκτελεί επαναλαμβανόμενη δισδιάστατη κλιμάκωση ενός ζεύγους συντεταγμένων.

Στη γεωμετρία, η ομοιόμορφη κλιμάκωση αναφέρεται στη μεγέθυνση ή τη σμίκρυνση διανυσμάτων ή σχημάτων προς την ίδια κατεύθυνση. Μπορεί να ερμηνευτεί ως πολλαπλασιασμός κάθε σημείου με μια σταθερά (συντελεστή κλιμάκωσης).

Για αυτή την άσκηση, υπόθεσε ότι οι τιμές κλιμάκωσης είναι μόνο θετικές.

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

3. Σύνθεσε συναρτήσεις μετασχηματισμού

Συνδύασε δύο συναρτήσεις μετασχηματισμού για να εκτελέσεις έναν επαναλαμβανόμενο μετασχηματισμό. Αυτό συχνά ονομάζεται σύνθεση συναρτήσεων, όπου το αποτέλεσμα της πρώτης συνάρτησης 'f(x)' χρησιμοποιείται ως είσοδος στη δεύτερη συνάρτηση '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. Αποθήκευσε τα αποτελέσματα των συναρτήσεων

Υλοποίησε τη συνάρτηση memoizeTransform. Παίρνει μια συνάρτηση προς απομνημόνευση και επιστρέφει μια νέα συνάρτηση που θυμάται τις εισόδους της συνάρτησης που της δόθηκε, ώστε η τελευταία τιμή επιστροφής να μπορεί να "θυμηθεί" και να υπολογιστεί μόνο μία φορά αν κληθεί ξανά με τα ίδια ορίσματα.

Η απομνημόνευση χρησιμοποιείται μερικές φορές στον δυναμικό προγραμματισμό. Επιτρέπει να γίνονται δαπανηρές πράξεις μόνο μία φορά, αφού τα αποτελέσματά τους θυμούνται. Σημείωση: σε αυτή την άσκηση θυμάται μόνο το τελευταίο αποτέλεσμα, σε αντίθεση με κάποιες λύσεις στον δυναμικό προγραμματισμό που απομνημονεύουν όλα τα αποτελέσματα.

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
Επεξεργασία μέσω GitHub Ο σύνδεσμος ανοίγει σε νέο παράθυρο ή καρτέλα
JavaScript Exercism

Έτοιμος να ξεκινήσεις την άσκηση Μετασχηματισμός συντεταγμένων;

Γράψου στο Exercism για να μάθεις και να κατακτήσεις JavaScript με 37 έννοιες159 ασκήσεις και πραγματική καθοδήγηση από ανθρώπους, όλα δωρεάν.