Dem Funktionör ist nichts zu schwör
Funktionen (in anderen Programmiersprachen auch Subroutinen oder Prozeduren genannt) sind eine Sammlung von Einzelbefehlen. Sie erlauben
Damit entsprechen Funktionen direkt der o.g. DRY-Philosophie ("Don't Repeat Yourself").
Eine Reihe von Funktionen gehören zum Grundinventar von R, andere können wir uns durch das Einbinden von Paketen dazuholen, oder wir machen uns unsere eigenen Funktionen!
Eine einfache Funktion ist
windows()
, die einfach ein Grafikfenster für zukünftige Ausgaben öffnet. Sie kommt ziemlich nackt daher, braucht keine Argumente und liefert nichts zurück. Wir benutzen sie wegen ihrer "Nebenwirkungen", nämlich das Öffnen eines Fensters.
Die aktuelle Zeit lässt sich mit Sys.time()
ermitteln.
Sys.time() #direkte Ausgabe des Rückgabewerts
zeit <- Sys.time() #Speichern des Rückgabe Wert
zeit #Ausgabe des gespeicherten Rückgabewerts
Diese Funktion liefert einen Rückgabewert (d.h. Output), den wir speichern und weiterverarbeiten können.
Wollen wir ein R-Objekt aus dem Speicher löschen, geht das mit der Funktion rm()
rm(zeit)
## Warning in rm(zeit): Objekt 'zeit' nicht gefunden
Diese Funktion benötigt ein Argument (d.h. Input). Dieses wird in den Klammern nach dem Funktionsnamen angegeben.
Außerdem gibt es Funktionen wie z.B. sin()
, die sowohl Argumente (Input) benötigen, als auch einen Rückgabewert (Output) liefern:
sin(1)
## [1] 0.841471
paste("erstesWort", "zweitesWort")
## [1] "erstesWort zweitesWort"
paste()
hat in diesem Fall sogar zwei Argumente bekommen und würde sich auch über noch mehr freuen.
Funktionen können also Argumente akzeptieren, Rückgabewerte liefern, und diese beiden Eigenschaften beliebig kombinieren.
R erlaubt, die Argumente einer Funktion mit Namen zu versehen:
plot(x=1, y=2)
Das Ergebnis ist identisch zu
plot(1, 2)
aber unterscheidet sich von
plot(y=1, x=2)
Bei unbenannten Argumenten "errät" R deren Rolle. Dies funktioniert ggf. auch mit unvollständig benanten Argumenten, oder letztlich über die Reihenfolge der Argumente. Bei Funktionen mit vielen Argumenten erleichtert es die Lesbarkeit des Codes, die Argumente zu benennen.
Eine eigene Funktion, die lästige wiederkehrende Arbeit vereinfacht, könnte so angelegt werden:
#einfache Funktion ohne Argumente und Rückgabewert
abwasch <- function()
{
print("Gläser spülen")
print("Teller spülen")
print("Besteck spülen")
}
Diese Funktionsdefinition enthält den Namen der Funktion (abwasch
), das Zuweisungszeichen und ein Schlüsselwort (<- function()
) sowie den eigentlich Funktionsrumpf, der durch geschweifte Klammern zusammengehalten wird, wie wir es auch schon von den Blöcken bei if()
oder den Schleifen kennen.
Nach dem Ausführen des Codes passiert scheinbar zunächst nichts. Aber die Funktion ist nun verfügbar -- sichtbar im Tab Environment in der rechten oberen Ecke von RStudio. Wir können uns auch über den Aufruf ihres Namens von ihrer Existenz überzeugen:
abwasch
## function()
## {
## print("Gläser spülen")
## print("Teller spülen")
## print("Besteck spülen")
## }
Wir sehen nur den zugehörigen Programmcode, aufgerufen und ausgeführt wird die Funktion, wenn der Aufruf mit den Klammern erfolgt:
abwasch()
## [1] "Gläser spülen"
## [1] "Teller spülen"
## [1] "Besteck spülen"
1.3 Aufgabe Abwasch-Marathon
Da Hausarbeit somit ihren Schrecken verloren hat, wasche doch bitte gleich die dreifache Menge ab!abwasch()
## [1] "Gläser spülen" ## [1] "Teller spülen" ## [1] "Besteck spülen"
abwasch()
## [1] "Gläser spülen" ## [1] "Teller spülen" ## [1] "Besteck spülen"
abwasch()
## [1] "Gläser spülen" ## [1] "Teller spülen" ## [1] "Besteck spülen"
oder eleganter
for (i in 1:3) abwasch()
## [1] "Gläser spülen" ## [1] "Teller spülen" ## [1] "Besteck spülen" ## [1] "Gläser spülen" ## [1] "Teller spülen" ## [1] "Besteck spülen" ## [1] "Gläser spülen" ## [1] "Teller spülen" ## [1] "Besteck spülen"
Damit die Funktion auf veränderte Bedingungen reagieren kann, sollte sie Eingabedaten über ihre Argumente berücksichtigen:
#Funktion mit Argument
abwasch2 <- function(was_noch)
{
print("Gläser spülen")
print("Teller spülen")
print("Besteck spülen")
print(paste(was_noch, "spülen"))
}
1.4 Aufgabe Abwaschen der Töpfe
Lasst uns nun zusätzlich noch Töpfe abspülen! Wie muss der Aufruf der Funktion aussehen?abwasch2("Töpfe") #Funktion mit Argument benutzen
## [1] "Gläser spülen" ## [1] "Teller spülen" ## [1] "Besteck spülen" ## [1] "Töpfe spülen"
Noch flexibler wird die Funktion, wenn man ihr einen Vektor von Zeichenketten übergeben kann, von denen jede dann einzeln abgewaschen wird. Wie muss die Funktion erweitert werden?#Funktion mit Argument abwasch2_viele <- function(was_noch) { print("Gläser spülen") print("Teller spülen") print("Besteck spülen") for (teil in was_noch) print(paste(teil, "spülen")) } abwasch2_viele(c("Untertassen", "Haare"))
## [1] "Gläser spülen" ## [1] "Teller spülen" ## [1] "Besteck spülen" ## [1] "Untertassen spülen" ## [1] "Haare spülen"
Funktionsargumente können auch optional sein:
paste("Karl", "Marx") #Grundeinstellung: mit Leerzeichen vernüpfen
## [1] "Karl Marx"
paste("Karl", "Marx", "Straße", sep="-") #mit optionalem Argument "sep", Vernüpfung mit "-"
## [1] "Karl-Marx-Straße"
In der Hilfe zu den Funktionen (?paste
) werden optionale Argumente mit ihrem voreingestellten Wert in der Zeile "Usage" aufgeführt. In der selben Art können wir auch für unsere eigene Funktion optionale Argumente erlauben.
1.4 Aufgabe Abwasch mit optionalem Mehr
Verändere die Funktionabwasch2()
so, dass ihr Argument optional wird. Die entsprechende Ausgabe soll nur erfolgen, wenn das Argument gesetzt ist.Wird ein optionales Argument nicht übergeben, nimmt es den Wert an, der in der Funktionsdeklaration angegeben wurde. Üblich ist in diesem Zusammenhang der Sonderwert
mein_argument = NULL
, was dann mitis.null(mein_argument)
abgefragt werden kann.abwasch2 <- function(was_noch=NULL) { print("Gläser spülen") print("Teller spülen") print("Besteck spülen") if (!is.null(was_noch)) print(paste(was_noch, "spülen")) } abwasch2() #Basisspülprogramm
## [1] "Gläser spülen" ## [1] "Teller spülen" ## [1] "Besteck spülen"
abwasch2("Toilette") #mit Extraspülung
## [1] "Gläser spülen" ## [1] "Teller spülen" ## [1] "Besteck spülen" ## [1] "Toilette spülen"
Unsere Funktion soll nun ein weiterverarbeitbares Ergebnis liefern. In unserem Fall soll dies der Wert sauberkeit
sein, die als Zufallszahl gezogen wird. Die Rückgabe des Wertes erfolgt mit dem Befehl return()
. Mit diesem wird die Funktion auch verlassen; nachfolgende Befehle werden nicht mehr ausgeführt:
#Funktion mit Argument und Rückgabewert
abwasch3 <- function(was_noch) #Funktion für den gesamten Abwasch definieren
{
print("Gläser spülen")
print("Teller spülen")
print("Besteck spülen")
print(paste0(was_noch, " spülen"))
sauberkeit <- runif(n=1, min = 1, max=10) #eine Zufallszahl zwischen 1 und 10
return(sauberkeit)
print("Spülbecken putzen") #Dieser Befehl wird nicht mehr erreicht
}
sauberkeit <- abwasch3("Töpfe") #Funktion mit Argument benutzen, Rückgabewert in Variable speichern
print(sauberkeit)
## [1] "Gläser spülen"
## [1] "Teller spülen"
## [1] "Besteck spülen"
## [1] "Töpfe spülen"
## [1] 7.067267
1.5 Aufgabe Abwaschwasser wechseln
Wasche ab, auch die Pfanne. Wenn die Sauberkeit unter 5 ist, gib die Meldung "Abwaschwasser wechseln!" aus, andernfalls melde "Fertig!"sauberkeit <- abwasch3("Pfanne")
## [1] "Gläser spülen" ## [1] "Teller spülen" ## [1] "Besteck spülen" ## [1] "Pfanne spülen"
if (sauberkeit < 5) { print("Abwaschwasser wechseln!") } else { print("Fertig!") }
## [1] "Abwaschwasser wechseln!"
Funktionen lassen sich im Hauptprogramm aufrufen, können aber auch innerhalb anderer Funktionen aufgerufen werden. Es ist sogar möglich, dass sich Funktionen selbst aufrufen (rekursive Funktionen).
1.6 Aufgabe Temperaturumrechnung
Schreibe eine Funktionfahr_zu_kelvin()
, die die Temperatur von Fahrenheit in Kelvin umrechnet \((T_{Kelv} = ((T_{Fahr} - 32) \cdot (5 / 9)) + 273.15)\)!Schreibe eine Funktionfahr_zu_kelvin <- function(temp_f) { temp_k <- ((temp_f - 32) * (5 / 9)) + 273.15 return(temp_k) } # Gefriehrpunkt von Wasser fahr_zu_kelvin(32)
## [1] 273.15
# Siedepunkt von Wasser fahr_zu_kelvin(212)
## [1] 373.15
kelvin_zu_celsius()
, die die Temperatur von Kelvin in °Celsius umrechnet (\(T_{Cels} = ((T_{Kelv} - 273.15\))!Schreibe nun eine Funktion, die Fahrenheit in Celsius umrechnet. verwende dabei die beiden bestehenden Funktionenkelvin_zu_celsius <- function(temp_k) { temp_c <- temp_k - 273.15 return(temp_c) } #absoluter Nullpunkt kelvin_zu_celsius(0)
## [1] -273.15
fahr_zu_kelvin
undkelvin_zu_celsius
!fahr_zu_celsius <- function(temp_f) { temp_k <- fahr_zu_kelvin(temp_f) temp_c <- kelvin_zu_celsius(temp_k) return(temp_c) } # Gefriehrpunkt von Wasser fahr_zu_celsius(32)
## [1] 0
Mein Tanzbereich, Dein Tanzbereich
Beim LESEN von Variablen (und auch Funktionen) wird i.d.R. "von innen nach außen" ausgewertet. Zuweisungen bleiben lokal:
X <- "externer Wert von X" #eine Zuweisung zur EXTERNEN /globalen Variablen X
Y <- "externer Wert von Y" #eine Zuweisung zur INTERNEN Variablen Y
print("Auswertung außen:")
print(paste("X:", X))
print(paste("Y:", Y))
meine_funktion <- function() {
X <- "interner Wert von X" #eine Zuweisung zur INTERNEN Variablen X
Z <- "interner Wert von Z" #eine Zuweisung zur INTERNEN Variablen Z
print("Auswertung innen:")
print(paste("X:", X))
print(paste("Y:", Y))
print(paste("Z:", Z))
}
meine_funktion() # Aufruf der Funktion
print("Auswertung außen:")
print(paste("X:", X))
print(paste("Y:", Y))
print(paste("Z:", Z))
## Error in paste("Z:", Z): Objekt 'Z' nicht gefunden
## [1] "Auswertung außen:"
## [1] "X: externer Wert von X"
## [1] "Y: externer Wert von Y"
## [1] "Auswertung innen:"
## [1] "X: interner Wert von X"
## [1] "Y: externer Wert von Y"
## [1] "Z: interner Wert von Z"
## [1] "Auswertung außen:"
## [1] "X: externer Wert von X"
## [1] "Y: externer Wert von Y"
Das heißt:
X
existiert praktisch doppelt, extern und intern. Die innere Zuweisung beeinflusst den Wert außen nicht.Y
wird nur außen zugewiesen, ist aber dennoch innerhalb der Funktion lesbarZ
wird nur innen zugewiesen. Außerhalb ist sie gar nicht verfügbar (Fehlermeldung).Die Veränderung globaler Variablen ist auch von innerhalb der Funktion möglich, wenn man statt <-
den Operator<<-
verwendet:
X <- "externer Wert von X" #eine Zuweisung zur EXTERNEN /globalen Variablen X
print("Auswertung außen:")
print(paste("X:", X))
meine_funktion <- function() {
X <- "interner Wert von X" #eine Zuweisung zur INTERNEN Variablen X
X <<- "interner Wert von X_außen" #eine Zuweisung zur GLOBALEN Variablen X
print("Auswertung innen:")
print(paste("X:", X))
}
meine_funktion() # Aufruf der Funktion
print("Auswertung außen:")
print(paste("X:", X))
## [1] "Auswertung außen:"
## [1] "X: externer Wert von X"
## [1] "Auswertung innen:"
## [1] "X: interner Wert von X"
## [1] "Auswertung außen:"
## [1] "X: interner Wert von X_außen"
Noch raffiniertere Kontrolle über den Gültigkeitsbereich von Auswertungen und Zuweisungen lassen sich mit get()
und assign()
realisieren.
Derartige Zuweisungen gelten jedoch als schlechter Programmierstil, da der Informationsfluss zwischen den Programmteilen dadurch sehr intransparent wird.
1.7 Aufgabe Gültigkeitsbereich
Welcher Wert wird im nachfolgenden Code ausgegeben? Bitte Lösung zunächst im Kopf herleiten, dann überprüfen.
x <- 1 meine_funktion <- function(x=2) { interne_funktion <- function(y) { x <- x+1 return(x) } x <- interne_funktion(x-1) return(x) } x <- meine_funktion() # Aufruf der Funktion x #?
## [1] 3