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

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

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

Εισαγωγή

Οι συναρτήσεις παρουσιάστηκαν νωρίτερα, στην έννοια Βασικά, με παραδείγματα όπως αυτό:

squareit <- function(x) {
    x * x
}

squareit(3)
#> [1] 9

# shorter form
squareit_short <- function(x) x ^ 2

Αν κοιτάξουμε πιο προσεκτικά τον ορισμό της squareit, μπορούμε να διακρίνουμε διάφορα μέρη:

  • Η συνάρτηση δέχεται ένα τυπικό όρισμα x.
  • Υπάρχει ένα σώμα συνάρτησης, συνήθως μέσα σε άγκιστρα { }.
  • Μόλις δημιουργηθεί το αντικείμενο συνάρτησης, ανατίθεται σε μια μεταβλητή squareit.

Μια συνάρτηση είναι αντικείμενο πρώτης τάξης στη R, όπως ακριβώς οι αριθμοί και οι συμβολοσειρές. Έτσι, το squareit <- function... είναι μια ανάθεση που συντακτικά μοιάζει ακριβώς με το x <- 42.

Ορίσματα

Η R δεν κάνει σαφή διάκριση ανάμεσα σε ορίσματα θέσης και ορίσματα με όνομα, σε αντίθεση με άλλες γλώσσες σεναρίων όπως η Python και η Julia.

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

f <- function(x, y) x / y

# call positionally
f(4, 2)
#> [1] 2

# call by name
f(y = 2, x = 4)
#> [1] 2

Προαιρετικά ορίσματα

Οι προεπιλεγμένες τιμές ορισμάτων μπορούν να οριστούν στον ορισμό της συνάρτησης, αλλά πρέπει να έρχονται μετά από όλα τα ορίσματα χωρίς προεπιλογή.

Μπορούμε τότε να επιλέξουμε αν θα δεχτούμε την προεπιλεγμένη τιμή ή θα την παρακάμψουμε.

g <- function(x, y = 2) x / y

# default y value
g(6)
#> [1] 3

# explicit y value
g(6, 3)
#> [1] 2

Επιπλέον ορίσματα

Για να δεχτείς έναν οποιονδήποτε αριθμό επιπλέον ορισμάτων, χρησιμοποίησε ένα ... (αποσιωπητικά) στον ορισμό. Είναι δυνατό να μετατρέψεις οποιεσδήποτε επιπλέον τιμές στην κλήση της συνάρτησης σε διάνυσμα, αλλά διάβασε παρακάτω για έναν εναλλακτικό τρόπο να χρησιμοποιείς αυτά τα "dot args" (ονομάζονται "varargs" σε αρκετές άλλες γλώσσες).

var_f <- function(x, y, ...) {
  print(c(...))
}

var_f(2, 3, "opt1", "opt2")
#> [1] "opt1" "opt2"

Περιβάλλον συνάρτησης

Προηγουμένως, είπαμε ότι τα τυπικά ορίσματα και το σώμα είναι και τα δύο μέρη μιας συνάρτησης.

Στην πραγματικότητα, υπάρχει και ένα τρίτο μέρος: το περιβάλλον στο οποίο ορίζεται η συνάρτηση.

Αυτό μπορεί να φανεί με την περίπτωση των εμφωλευμένων συναρτήσεων:

outer_func <- function(x) {
  inner_func <- function(y) {
    x * y
  }
  
  inner_func(3)
}

outer_func(5)
#> [1] 15

Η κλήση της συνάρτησης περνά το x = 5 στην εξωτερική συνάρτηση, και αυτή η τιμή είναι διαθέσιμη μέσα στο σώμα εκείνης της συνάρτησης.

Η εσωτερική συνάρτηση είναι μέρος του σώματος της εξωτερικής συνάρτησης και έχει πρόσβαση στην τιμή του x. Με άλλα λόγια, το x = 5 βρίσκεται στο περιβάλλον της εσωτερικής συνάρτησης.

Τεχνικά, αυτό είναι γνωστό ως κλειστότητα.

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

f_var <- function(x, ...) {
  sum(x, ...)
}

x <- c(1, 2, NA, 6)

# for sum(), na.rm defaults to FALSE
f_var(x)
#> [1] NA

# pass through the na.rm value
f_var(x, na.rm = TRUE)
#> [1] 9

Αυτή η τεχνική χρησιμοποιείται εκτενώς από βιβλιοθήκες του Tidyverse, όπως το stringr. Πολλές από τις συναρτήσεις του stringr είναι ένα φιλικό προς τον χρήστη περιτύλιγμα γύρω από συναρτήσεις χαμηλού επιπέδου από το stringi και τη βασική R.

Τα επιπλέον ορίσματα που δίνονται στις συναρτήσεις str_*() απλώς περνούν σε εκείνες τις συναρτήσεις χαμηλότερου επιπέδου.

Συναρτήσεις ως τιμές επιστροφής

Μια εξωτερική συνάρτηση μπορεί να ορίσει μια εσωτερική συνάρτηση (είτε με όνομα είτε ανώνυμη, όπως περιγράφεται παρακάτω) και να τη χρησιμοποιήσει ως τιμή επιστροφής.

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

times_y <- function(x) {
  # anonymous function - see next section
  \(y) x * y
}

f <- times_y(3)
class(f)
#> [1] "function"

f(4)
#> [1] 12

Ανώνυμες συναρτήσεις

Όταν ορίζουμε μια συνάρτηση, συνήθως δεσμεύουμε το αντικείμενο συνάρτησης που προκύπτει σε μια μεταβλητή.

x <- 2

# function definition captures x from the environment
doubleit_short <- function(y) x * y

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

Η χρήση ανώνυμων συναρτήσεων είναι τόσο συνηθισμένη που (από την έκδοση R v4.1.0) υπάρχει μια συντομευμένη σύνταξη για τον ορισμό τους: αντικατέστησε τη λέξη function με μια ανάστροφη κάθετο \.

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

Αντιγραφή κατά την τροποποίηση

Η R επιτρέπει ανάθεση σε μεμονωμένα στοιχεία ενός διανύσματος. Αν περάσουμε ένα διάνυσμα ως όρισμα συνάρτησης και το τροποποιήσουμε μέσα στο σώμα της συνάρτησης πριν το επιστρέψουμε, παίρνουμε ένα τροποποιημένο διάνυσμα.

Αλλά τι συνέβη στο αρχικό διάνυσμα;

f <- function(vec) {
  vec[1] <- 42
  vec
}

vals <- c(1, 3, 4)

# f() returns a modified vector
f(vals)
#> [1] 42  3  4  

# the original vector is unchanged.
vals
#> [1] 1 3 4

Η R είναι μια γλώσσα σχεδιασμένη για την επιστήμη δεδομένων. Η συλλογή αυτών των δεδομένων μπορεί να κοστίσει πολύ σε χρόνο, κόπο και ενδεχομένως ένα ιλιγγιώδες ποσό χρημάτων: είναι σημαντικό να μην τα αλλοιώσεις!

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

Η αντιγραφή μεγάλων δομών δεδομένων μπορεί να είναι υπολογιστικά δαπανηρή, αλλά γενικά αυτό είναι το μικρότερο κακό όταν η εναλλακτική είναι η αλλοίωση των δεδομένων.

Caution

Πρόσεχε τις πράξεις που οδηγούν σε επανειλημμένη αντιγραφή των ίδιων δεδομένων.

Παραφράζοντας τον (συγγραφέα του Tidyverse) Hadley Wickham:

Οι βρόχοι δεν είναι από τη φύση τους αργοί, αλλά κάνουν επικίνδυνα εύκολο να συμπεριλάβεις αργές πράξεις μέσα στον βρόχο.

Η διανυσματοποίηση ή οι συναρτήσεις ανώτερης τάξης μπορούν να σε βοηθήσουν να προστατευτείς από αυτού του είδους τον φονιά της απόδοσης.

Οδηγίες

Η εταιρεία σου ασχολείται με δεδομένα χαρτογράφησης για να φτιάχνει ιστοσελίδες. Μετά από κάποια συζήτηση, αποφασίστηκε να αρχίσετε να χρησιμοποιείτε την R για να κάνετε κάποιους υπολογισμούς δυναμικά.

Προσπάθησε να δουλέψεις απευθείας με τα σημεία ως διανύσματα: δε χρειάζεται να τα αναλύσεις σε ξεχωριστές συντεταγμένες x και y.

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

Υλοποίησε τη συνάρτηση scale(point, s), που παίρνει ένα σημείο με οποιονδήποτε αριθμό διαστάσεων και το κλιμακώνει με το προκαθορισμένο s.

# A 2D example
s <- c(0.5, 0.8)
point <- c(10, 5)
scale(point, s)
#> [1] 5 4

2. Μετάθεσε τις συντεταγμένες

Υλοποίησε τη συνάρτηση translate(point, ...), που επιστρέφει ένα νέο σημείο, με κάθε συντεταγμένη μετατοπισμένη κατά τις τιμές που δίνονται στα τρία σημεία.

Αυτή η συνάρτηση πρέπει να μπορεί να χειρίζεται σημεία οποιασδήποτε διάστασης, με τον αριθμό των ορισμάτων που περνάς στα τρία σημεία να αντιστοιχεί σε αυτή τη διάσταση.

point2d <- c(2, 3)

# supply dx, dy translations
translate(point2d, 0.5, 0.6)
#> [1] 2.5 3.6

point3d <- c(2, 3, 4)

# supply dx, dy, dz translations
translate(point3d, 0.5, 0.6, 0.7)
#> [1] 2.5 3.6 4.7

3. Μετασχημάτισε ένα σημείο 2D

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

Υλοποίησε τη συνάρτηση transform2d(dx, dy, s), που επιστρέφει μια συνάρτηση η οποία αξιοποιεί ένα closure για να εκτελεί επαναλαμβανόμενη μετάθεση και κλιμάκωση ενός σημείου σε 2D.

Το όρισμα κλιμάκωσης s πρέπει να είναι προαιρετικό, με προεπιλεγμένη τιμή 1.

Η συνάρτηση που επιστρέφεται πρέπει να παίρνει ένα σημείο 2D και μετά:

  1. να το μεταθέτει κατά τα προκαθορισμένα dx και dy
  2. να το κλιμακώνει με το s
# scale all coordinates by the same amount (2) in this example
# your code needs to be able to handle a vector `s`

tf2d <- transform2d(0.5, 0.6, 2)
class(tf2d)
#> [1] "function"

tf2d(c(2, 3))
#> [1] 5.0 7.2

# Accept default for `s`
tf2d <- transform2d(0.5, 0.6)

tf2d(c(2, 3))
#> 2.5 3.6

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

4. Μετασχημάτισε ένα σημείο 3D

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

Υλοποίησε τη συνάρτηση transform3d(dx, dy, dz, s), που επιστρέφει μια συνάρτηση.

Το όρισμα κλιμάκωσης s πρέπει να είναι προαιρετικό, με προεπιλεγμένη τιμή 1.

Η συνάρτηση που επιστρέφεται πρέπει να παίρνει ένα σημείο 3D, να το μεταθέτει κατά τις προκαθορισμένες τιμές και μετά να το κλιμακώνει με το s.

# using default `s`
tf3d <- transform3d(0.5, 0.6, 0.7)
point <- c(2, 3, 4)
tf3d(point)
#> [1] 2.5 3.6 4.7

# using vector `s`
tf3d <- transform3d(0.5, 0.6, 0.7, c(0.5, 0.6, 0.7))
tf3d(point)
#> [1] 1.25 2.16 3.29
Επεξεργασία μέσω GitHub Ο σύνδεσμος ανοίγει σε νέο παράθυρο ή καρτέλα
R Exercism

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

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