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

Koordináta-transzformáció

Tanulófeladat

Bevezetés

A függvényeket már az Alapok fogalomnál bemutattuk, ilyen példákon keresztül:

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

squareit(3)
#> [1] 9

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

Ha közelebbről megnézzük a(z) squareit definícióját, több részt is azonosíthatunk:

  • A függvény fogad egy formális paramétert, x-et.
  • Van egy függvénytörzs, általában kapcsos zárójelben { }.
  • Amint a függvényobjektum létrejön, hozzárendeljük egy squareit nevű változóhoz.

Az R-ben a függvény első osztályú objektum, akárcsak a számok és a stringek. Így a(z) squareit <- function... értékadás, szintaktikailag pontosan olyan, mint az x <- 42.

Argumentumok

Az R nem tesz éles különbséget a pozicionális és a kulcsszavas argumentumok között, ellentétben más szkriptnyelvekkel, például a Pythonnal és a Julia-val.

A függvényhívásokban az értékeket akár pozicionálisan, akár név szerint átadhatod. Utóbbi hasznos sok argumentummal rendelkező összetett függvényeknél, ahol nehéz megjegyezni a sorrendet.

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

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

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

Opcionális argumentumok

Alapértelmezett paraméterértékeket a függvénydefinícióban adhatsz meg, de ezeknek az összes alapérték nélküli paraméter után kell állniuk.

Ezután eldöntheted, hogy elfogadod az alapértéket, vagy felülírod.

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

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

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

További argumentumok

Ha tetszőleges számú további argumentumot szeretnél fogadni, használj ... (három pont) jelet a definícióban. A függvényhívásban átadott bármely extra értéket vektorrá alakíthatod, de olvass tovább egy alternatív módért ezeknek a „dot args”-oknak a használatára (amit számos más nyelvben „varargs”-nak neveznek).

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

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

A függvény környezete

Korábban azt mondtuk, hogy a formális paraméterek és a törzs egyaránt egy függvény komponensei.

Valójában van egy harmadik komponens is: a környezet, amelyben a függvényt definiáljuk.

Ezt az egymásba ágyazott függvények példáján lehet szemléltetni:

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

outer_func(5)
#> [1] 15

A függvényhívás x = 5 értéket ad át a külső függvénynek, és ez az érték elérhető a függvénytörzsön belül.

A belső függvény a külső függvénytörzs része, és hozzáfér az x értékéhez. Másképp fogalmazva, x = 5 a belső függvény környezetében van.

Technikailag erre a closure kifejezést használják.

A környezet különösen fontos a dot args esetében, mivel az így átadott értékek továbbadhatók a függvénytörzsben lévő függvényhívásoknak. A külső függvénynek nem kell tudnia vagy törődnie azzal, hogy mit jelentenek a 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

Ezt a technikát széles körben használják a Tidyverse könyvtárai, például a stringr. A stringr számos függvénye felhasználóbarát burkoló a stringi és az alap R alacsony szintű függvényei körül.

A str_*() függvényeknek átadott extra argumentumok egyszerűen továbbkerülnek ezekhez az alacsony szintű függvényekhez.

Függvények visszatérési értékként

Egy külső függvény definiálhat egy belső függvényt (akár nevesítettet, akár névtelent, ahogy alább le van írva), és visszatérési értékként használhatja.

A visszaadott függvény magában foglalja majd azt a környezetet, amelyben definiálták.

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

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

f(4)
#> [1] 12

Névtelen függvények

Amikor függvényt definiálunk, általában a létrejövő függvényobjektumot egy változóhoz rendeljük.

x <- 2

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

Ez megkönnyíti a függvény későbbi használatát a szkriptben, de az ilyen hozzárendelés nem szükséges. Egy rövid, egyszer használatos függvény hasznos lehet az adott helyzetben. Névhez kötés nélkül névtelen függvénynek nevezzük.

A névtelen függvények használata annyira gyakori, hogy (az R v4.1.0 óta) létezik egy rövidített szintaxis a definiálásukra: cseréld le a function szót egy backslashre, \.

Erre láttunk példát az előző szakaszban, a visszatérési értékeknél.

Másolás módosításkor

Az R lehetővé teszi egy vektor egyes elemeihez történő értékadást. Ha egy vektort argumentumként adunk át egy függvénynek, és a függvénytörzsben módosítjuk, mielőtt visszaadnánk, egy módosított vektort kapunk.

De mi történt az eredeti vektorral?

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

Az R egy adatudományra tervezett nyelv. Ezen adatok összegyűjtése sok időbe, erőfeszítésbe és esetleg fájdalmasan sok pénzbe kerülhet: fontos, hogy ne sérüljenek meg!

Az általános elv (néhány kivételtől eltekintve) a másolás módosításkor. Ha egy objektumot (például egy vektort) olyan módon változtatunk meg, ami később problémákat okozhat, az R egy módosított másolatot ad vissza, és az eredetit érintetlenül hagyja.

Nagy adatszerkezetek másolása számításigényes lehet, de ez általában a kisebbik rossz, ha az alternatíva az adatok megsérülése.

Caution

Vigyázz az olyan műveletekkel, amelyek ugyanazon adatok ismételt másolásához vezetnek.

Hadley Wickham (a Tidyverse szerzője) szavait parafrazálva:

A ciklusok önmagukban nem lassúak, de veszélyesen könnyűvé teszik, hogy lassú műveleteket tegyünk a ciklusba.

A vektorizáció vagy a magasabb rendű függvények segíthetnek megvédeni az ilyen típusú teljesítménygyilkosoktól.

Utasítások

A céged térképi adatokkal dolgozott, hogy weboldalakat építsen. Egy megbeszélés után úgy döntöttek, hogy elkezdik használni az R-t néhány számítás dinamikus elvégzésére.

Próbálj közvetlenül a vektoros pontokkal dolgozni: nincs szükség arra, hogy külön x és y koordinátákra bontsd ki őket.

1. Skálázd a koordinátákat

Valósítsd meg a scale(point, s) függvényt, amely egy tetszőleges számú dimenzióval rendelkező pontot vesz át, és az előre meghatározott s értékkel skálázza.

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

2. Told el a koordinátákat

Valósítsd meg a translate(point, ...) függvényt, amely egy új pontot ad vissza, ahol minden koordináta a három pontban megadott értékekkel tolódik el.

A függvénynek bármilyen dimenziójú pontot kezelnie kell, ahol a három pontban átadott argumentumok száma megegyezik a pont dimenziójával.

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. Transzformálj egy 2D pontot

Néhány csapattársad kevésbé jártas az R-ben, ezért úgy döntesz, hogy egy függvénylezárást használsz, hogy újrafelhasználható transzformációkat készíts {x, y} koordinátapárokhoz.

Valósítsd meg a transform2d(dx, dy, s) függvényt, amely egy olyan függvényt ad vissza, amely egy függvénylezárást használva ismételhető 2D eltolást és skálázást végez egy ponton.

Az s skálázási argumentum opcionális, alapértéke 1.

A visszaadott függvény egy 2D pontot vesz át, majd:

  1. eltolja az előre meghatározott dx és dy értékkel
  2. skálázza s-sel
# 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

A műveletek sorrendje fontos: az eltolás, majd a skálázás adja a helyes eredményt, a skálázás, majd az eltolás nem. Technikai nyelven szólva a műveletek nem kommutatívak.

4. Transzformálj egy 3D pontot

A térképi adatok magasságokat is tartalmazhatnak, amelyeket árnyékolás és szintvonalak képernyőn való megjelenítésére használnak.

Valósítsd meg a transform3d(dx, dy, dz, s) függvényt, amely egy függvényt ad vissza.

Az s skálázási argumentum opcionális, alapértéke 1.

A visszaadott függvény egy 3D pontot vesz át, eltolja az előre meghatározott értékekkel, majd skálázza s-sel.

# 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
Szerkesztés GitHubon A hivatkozás új ablakban vagy lapon nyílik meg
R 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) R nyelvet 21 fogalom111 feladat segítségével, valódi emberi mentorálással, mindez ingyen.