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.
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)
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
## <srcref: file "" chars 2:12 to 7:1>
Wir sehen nur den zugehörigen Programmcode. Aufgerufen und ausgeführt wird die Funktion aber erst, wenn der Aufruf mit den Klammern erfolgt:
abwasch()
## [1] "Gläser spülen"
## [1] "Teller spülen"
## [1] "Besteck spülen"
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"))
}
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.
Aufgabe Abwasch mit optionalem Mehr
Verändere die Funktion
abwasch2()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] 6.140736
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] "Fertig!"
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).
Aufgabe Temperaturumrechnung
Schreibe eine Funktion
fahr_zu_kelvin(), die die Temperatur von Fahrenheit in Kelvin umrechnet \((T_{Kelv} = ((T_{Fahr} - 32) \cdot (5 / 9)) + 273.15)\)!
fahr_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.15Schreibe eine Funktion
kelvin_zu_celsius(), die die Temperatur von Kelvin in °Celsius umrechnet (\(T_{Cels} = ((T_{Kelv} - 273.15\))!kelvin_zu_celsius <- function(temp_k) { temp_c <- temp_k - 273.15 return(temp_c) } #absoluter Nullpunkt kelvin_zu_celsius(0)## [1] -273.15Schreibe nun eine Funktion, die Fahrenheit in Celsius umrechnet. verwende dabei die beiden bestehenden Funktionen
fahr_zu_kelvinundkelvin_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 EXTERNEN Variablen Y
print("Auswertung außerhalb der Funktion:")
print(paste("X:", X)) #liefert externen Wert
print(paste("Y:", Y)) #liefert externen Wert
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)) #liefert internen Wert
print(paste("Y:", Y)) #liefert externen Wert
print(paste("Z:", Z)) #liefert internen Wert
}
meine_funktion() # Aufruf der Funktion
print("Auswertung außen:")
print(paste("X:", X)) #liefert externen Wert
print(paste("Y:", Y)) #liefert externen Wert
print(paste("Z:", Z)) #Fehlermeldung, da Z außen nicht existiert
## Error: Objekt 'Z' nicht gefunden
## [1] "Auswertung außerhalb der Funktion:"
## [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 lesbar.Z 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 EXTERNEN 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.