Kurzusok
/
R
R
/
Tanterv
/
Függvények
Fü

Függvények ebben a kurzusban: R

1 feladat

A(z) Függvények fogalomról

A függvényekkel már az Alapok fogalomnál találkoztunk, ilyen példákkal:

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 squareit definícióját, több részt is azonosíthatunk benne:

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

R-ben a függvény első osztályú objektum, akárcsak a számok vagy a stringek. A squareit <- function... tehát olyan értékadás, amely szintaktikailag pontosan olyan, mint az x <- 42.

Advanced

A függvények összetevőihez hozzáférni ritkán szoktunk, de meglehetősen egyszerű.

class(squareit)
#> [1] "function"
formals(squareit)
#> $x

body(squareit)
#> {
#>     x * x
#> }

A formals() függvénnyel lekérdezett argumentumok egy listához hasonlítanak, a $x szintaxissal. Valójában a típusuk pairlist, a listák egy különleges fajtája, amely kulcs-érték párokat tartalmaz.

A body futtatható kód, a típusa language (ez valószínűleg nem az, amivel neked foglalkoznod kell).

Argumentumok

Az R nem tesz éles különbséget a pozicionális és a név szerinti argumentumok között, ellentétben más szkriptnyelvekkel, például a Pythonnal vagy a Juliával.

Egy függvényhívásban az értékeket átadhatjuk pozicionálisan vagy név szerint is. Ez utóbbi bonyolult, sok argumentumot váró függvényeknél hasznos, ahol nehéz megjegyezni az argumentumok sorrendjét.

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

A függvénydefinícióban megadhatunk alapértelmezett argumentumértékeket, de ezeknek az összes alapérték nélküli argumentum után kell következniük.

Ezután eldönthetjük, hogy elfogadjuk-e az alapértéket, vagy felülírjuk.

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

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

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

Extra argumentumok

Ha tetszőleges számú további argumentumot szeretnénk fogadni, használjunk ... (ellipszis) jelet a definícióban. A függvényhívásban átadott extra értékeket vektorrá is alakíthatjuk, de olvass tovább, mert mutatunk egy másik módot ezeknek a »pontargumentumoknak« a használatára (más nyelvekben »varargs« a nevük).

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 argumentumok és a törzs is a függvény összetevői.

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

Ezt az egymásba ágyazott függvények példájával szemléltethetjük:

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

outer_func(5)
#> [1] 15

A függvényhívás átadja az x = 5 értéket a külső függvénynek, és ez az érték elérhető annak 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 megfogalmazva: az x = 5 a belső függvény környezetében van.

Technikailag ezt closure-nak nevezik.

A környezet különösen fontos a pontargumentumoknál, mert 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, sőt nem is kell érdekelnie, hogy mit jelentenek a pontargumentumok.

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 az stringi és a base R alacsony szintű függvényei köré épülő, felhasználóbarát burkoló. A str_*() függvényeknek átadott extra argumentumok egyszerűen továbbadódnak ezeknek az alacsonyabb szintű függvényeknek.

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

Egy külső függvény definiálhat egy belső függvényt (nevesítettet vagy névtelent, ahogy azt lentebb leírjuk), és visszatérési értékként használhatja azt.

A visszaadott függvény magában hordozza 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, a létrejövő függvényobjektumot általában egy változóhoz kötjü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ákötés nem szükségszerű. Egy rövid, csak egyszer használt 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-tól) van egy rövidebb szintaxis a definiálásukra: a function szót cseréld le egy visszaperjelre: \.

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-ben egy vektor egyes elemeihez is hozzárendelhetünk értéket. 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 adattudományra tervezett nyelv. Az adatok összegyűjtése sok időbe és energiába kerülhet, és potenciálisan fájdalmasan sok pénzbe: ezért fontos, hogy ne sérüljenek meg!

Az általános szabály (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át okozhatna, az R egy módosított másolatot ad vissza, az eredetit pedig é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 azokkal a műveletekkel, amelyek ugyanazon adatok ismételt másolásához vezetnek.

(A Tidyverse szerzőjét) Hadley Wickhamet 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álás vagy a magasabb rendű függvények segíthetnek megvédeni ettől a fajta teljesítménygyilkostól.

Szerkesztés GitHubon A hivatkozás új ablakban vagy lapon nyílik meg

Tanuld meg a(z) Függvények fogalmat