Die Mehrfachalternative in R: die Funktion switch()

Die Mehrfachalternative l├Ąsst sich durch verschachtelte Alternativen realisieren, was aber oft zu umst├Ąndlichen und fehleranf├Ąlligen Quelltexten f├╝hrt. ├ťbersichtlicher ist die Realisierung mit der Funktion switch(). F├╝r sie gibt es in R zwei Versionen, die sich in der Bedingungspr├╝fung unterscheiden: entweder wird eine Zeichenkette ausgewertet (character-Version) oder das Ergebnis der Bedingungspr├╝fung wird in eine ganze Zahl verwandelt (integer-Version).
Noch keine Stimmen abgegeben
Noch keine Kommentare

Einordnung des Artikels

Einf├╝hrung

Bei einer Alternative kann eine von zwei M├Âglichkeiten eintreten; welche M├Âglichkeit eintritt, wird durch eine Bedingungspr├╝fung festgestellt. Und abh├Ąngig davon welche M├Âglichkeit eintritt, werden andere Programmteile ausgef├╝hrt (siehe Abbildung 1 links; je nachdem ob die Bedingung erf├╝llt ist oder nicht, wird Anweisung 2 oder Anweisung 3 ausgef├╝hrt).

Abbildung 1: Links: Darstellung der Alternative, bei der durch eine Bedingungspr├╝fung entschieden wird, welche von 2 Anweisungen ausgef├╝hrt wird. Rechts: Bei der Mehrfachalternative wird nach der Bedingungspr├╝fung einer von im Prinzip beliebig vielen Zweigen ausgew├Ąhlt (hier sind es 4 Zweige).Abbildung 1: Links: Darstellung der Alternative, bei der durch eine Bedingungspr├╝fung entschieden wird, welche von 2 Anweisungen ausgef├╝hrt wird. Rechts: Bei der Mehrfachalternative wird nach der Bedingungspr├╝fung einer von im Prinzip beliebig vielen Zweigen ausgew├Ąhlt (hier sind es 4 Zweige).

Bei einer Mehrfachalternative k├Ânnen beliebig viele M├Âglichkeiten eintreten (siehe Abbildung 1 rechts; hier gibt es vier M├Âglichkeiten, von denen eine wieder durch eine Bedingungspr├╝fung ausgew├Ąhlt wird). Es ist klar, dass man eine Mehrfachalternative durch geeignet viele Alternativen realisieren kann. Allerdings f├╝hrt dies zu umst├Ąndlichen und fehleranf├Ąlligen Quelltexten. Es gibt daher eine eigene Funktion switch(), die pr├╝ft, welche von mehreren M├Âglichkeiten zutrifft.

Die Implementierung der Funktion switch() h├Ąngt aber davon ab, wie die Bedingungspr├╝fung realisiert ist; dazu gibt es zwei M├Âglichkeiten:

  1. Entweder wird gepr├╝ft, welchen Wert eine ganze Zahl annimmt (man nennt dies die integer-Version von switch()).
  2. Oder es wird gepr├╝ft, welchen Wert eine Zeichenkette hat (man nennt dies die character-Version von switch()).

In den folgenden Abschnitten werden diese beiden Versionen vorgestellt, dazu wird ein sehr einfaches Beispiel gew├Ąhlt:

  1. Es ist eine ganze Zahl von 10 bis 15 gegeben und es soll die zugeh├Ârige Hexadezimalziffer von A bis F bestimmt werden.
  2. Es ist die Hexadezimalziffer (von A bis F gegeben) und es soll die zugeh├Ârige Zahl (von 10 bis 15) bestimmt werden.

Die Aufgabe l├Ąsst sich auch ohne die Funktion switch() l├Âsen ÔÇô und diese L├Âsung ist sogar viel einfacher. Aber um kennenzulernen, wie man die Funktion switch() einsetzt, ist diese Beispiel bestens geeignet.

Umrechnung zwischen einer Zahl und einer Hexadezimalziffer

Beim Umrechnen zwischen Dezimalzahlen und Hexadezimalzahlen ben├Âtigt man die Zuordnung zwischen den Zahlen von 10 bis 15 zu den Hexadezimalziffern von A bis F, die man folgender Tabelle entnehmen kann:

# 10  ||  A 
# 11  ||  B 
# 12  ||  C 
# 13  ||  D 
# 14  ||  E 
# 15  ||  F

Da es in R die Konstanten letters und LETTERS gibt, l├Ąsst sich das Problem der Umrechnung (in beiden Richtungen) leicht l├Âsen. Die Konstanten sind:

letters[1:6]
# [1] "a" "b" "c" "d" "e" "f"

LETTERS[1:6]
# [1] "A" "B" "C" "D" "E" "F"

Die Berechnung der Hexadezimalziffer lautet dann zum Beispiel (hier werden de Gro├čbuchstaben verwendet):

# n als integer gegeben, zum Beispiel
n <- 10

hexdigit <- character(length = 1)
if(n > 9 && n < 16){
  hexdigit <- LETTERS[n - 9]
}
hexdigit
# [1] "A"

Die umgekehrte Berechnung k├Ânnte lauten:

# hexdigit als character gegeben, zum Beispiel

hexdigit = "A"

n <- 9 + which(LETTERS == hexdigit)
n
# [1] 10

Wie man diese beiden Umrechnungen mit der Funktion switch() durchf├╝hrt, wird in den folgenden Abschnitten gezeigt.

Die integer-Version von switch()

Die Funktion switch() besitzt als Eingabewerte EXPR und ... :

switch(EXPR, ...)

Dabei steht EXPR f├╝r den Wert (oder Ausdruck) der gepr├╝ft werden soll; in der integer-Version muss hier eine ganze Zahl stehen. (Was passiert, wenn man eine Gleitkommazahl eingibt, wird weiter unten gezeigt.)

Das Argument ... gibt die verschiedenen M├Âglichkeiten der Mehrfachalternative an:

  • Ist EXPR == 1 , wird die erste M├Âglichkeit ausgew├Ąhlt,
  • ist EXPR == 2 , wird die zweite M├Âglichkeit ausgew├Ąhlt,
  • und so weiter.

Man erkennt daran sofort, dass die Alternativen nicht zu einem Vektor zusammengefasst werden d├╝rfen, denn dann gibt es nur f├╝r EXPR == 1 eine Auswahl, n├Ąmlich den Vektor.

Das folgende Skript zeigt die Realisierung f├╝r das Beispiel der Berechnung der Hexadezimalziffer:

# n als integer gegeben, zum Beispiel
n <- 10

hexdigit <- switch(EXPR = (n - 9), 'A', 'B', 'C', 'D', 'E', 'F')
hexdigit
# [1] "A"

n <- 16
hexdigit <- switch(EXPR = (n - 9), 'A', 'B', 'C', 'D', 'E', 'F')
hexdigit
# NULL

Zeile 4: Das Argument EXPR wird gleich n - 9 gesetzt; wenn daher n die Werte 10, 11, ..., 15 annimmt, ist n - 9 gleich 1, 2, ..., 6. F├╝r das Argument ... werden die 6 Hexadezimalziffern von A bis F gesetzt.

Zeile 2, 5 und 6: F├╝r n = 10 erh├Ąlt man "A" .

Zeile 8 bis 11: Dagegen gibt es zu n = 16 keine Zuordnung im Argument ... und die Funktion switch() liefert NULL als R├╝ckgabewert.

Naheliegend ist die Frage, welchen Wert switch() liefert, wenn oben etwa n <- 10.5 gesetzt wird:

n <- 10.5

hexdigit <- switch(EXPR = (n - 9), 'A', 'B', 'C', 'D', 'E', 'F')
hexdigit
# [1] "A"

In diesem Fall wird die Gleitkommazahl n - 9 in eine ganze Zahl verwandelt und dabei werden Nachkommastellen abgeschnitten. Das hei├čt EXPR wird jetzt mit 1 verarbeitet, was zum Ergebnis "A" f├╝hrt.

Eine weitere Fehlerquelle ist es, statt den 6 Zeichen f├╝r ... den Vektor LETTERS[1:6] zu setzen. Das folgende Skript zeigt, wie switch() jetzt die Eingaben verarbeitet:

# n als integer gegeben, zum Beispiel
n <- 10

hexdigit <- switch(EXPR = (n - 9), LETTERS[1:6])
hexdigit
# [1] "A" "B" "C" "D" "E" "F"

n <- 15
hexdigit <- switch(EXPR = (n - 9), LETTERS[1:6])
hexdigit
# NULL

Zeile 2 bis 6: In ... gibt es nur eine M├Âglichkeit. Daher wird n = 10 zum kompletten Vektor LETTERS[1:6] ausgewertet.

Zeile 8 bis 11: F├╝r n = 15 gibt es keine Zuordnung und es wird NULL zur├╝ckgegeben.

Dass NULL zur├╝ckgegeben wird, wenn keine ├ťbereinstimmung zwischen EXPR und einer Komponente in ... besteht, m├Âchte man gerne vermeiden und stattdessen einen default-Wert zur├╝ckgeben. In der integer-Version von switch() kann man aber keinen default-Wert setzen; dies ist nur in der character-Version m├Âglich.

Die character-Version von switch()

In der integer-Version von switch() liefert EXPR einen Zahlenwert i, der die i-te Komponente aus den Argumenten ... ausw├Ąhlt. In der character-Version muss eine Zeichenkette die Zuordnung herstellen. Dies geschieht dadurch, dass die Argumente ... mit Namen versehen werden: Die Zuordnung erfolgt von der Zeichenkette zum Namen.

Das folgende Skript demonstriert dies f├╝r das Beispiel der Hexadezimalziffern. Dabei ist eine Hexadezimalziffer gegeben (von A bis F) und im Argument ... von switch() sind die 6 Zahlen (von 10 bis 15) mit Namen versehen; die Namen stimmen gerade mit den m├Âglichen Werten von EXPR ├╝berein:

# digit als Zeichen gegeben, zum Beispiel
digit <- 'A'

n <- switch(EXPR = digit, A = 10, B = 11, C = 12, D = 13, E = 14, F = 15)
n
# [1] 10

digit <- 'F'

n <- switch(EXPR = digit, A = 10, B = 11, C = 12, D = 13, E = 14, F = 15)
n
# [1] 15

digit <- 'G'
n <- switch(EXPR = digit, A = 10, B = 11, C = 12, D = 13, E = 14, F = 15)
n
# NULL

Zeile 2 bis 5: Die Hexadezimalziffer 'A' stimmt mit dem Namen der ersten Komponente in ... ├╝berein, daher wird n gleich 10 gesetzt.

Zeile 14 bis 17: Kann die Auswertung von EXPR keinem Namen zugeordnet werden, gibt switch() NULL zur├╝ck.

Den letzten Fall m├Âchte man oft vermeiden und stattdessen selbst einen geeigneten Wert setzen. Aber wie soll man f├╝r "alle anderen Werte" eine Zuordnung definieren? Dies geschieht mit Hilfe eines default-Wertes, der als letzter Wert von ... gesetzt wird, der aber keinen Namen erh├Ąlt.

Im folgenden Skript wird NA als default-Wert gesetzt, wenn keine Zuordnung hergestellt werden kann:

digit <- 'G'

n <- switch(EXPR = digit, A = 10, B = 11, C = 12, D = 13, E = 14, F = 15, NA)
n
# [1] NA

Man k├Ânnte nat├╝rlich auch jeden anderen Wert anstelle von NA verwenden; wichtig ist nur, dass er keinen Namen erh├Ąlt.

Der R├╝ckgabewert von switch()

Die obigen Beispiele waren in dem Sinn speziell, dass stets von einer Zahl zu einem Zeichen oder umgekehrt zugeordnet wurde. Es sollte aber klar sein, dass man im Argument ... Objekte mit beliebigem Datentyp setzen kann. Es ist nicht einmal n├Âtig, dass die Objekte in ihrem Datentyp ├╝bereinstimmen. Wenn keine Zuordnung m├Âglich ist, wird NULL zur├╝ckgeben

x <- switch(EXPR = 1, 'A', 17)
x
# [1] "A"

y <- switch(EXPR = 2, 'A', 17)
y
# [1] 17

z <- switch(EXPR = 3, 'A', 17)
z
# NULL

Realisiert die Funktion switch() die Mehrfachalternative?

Abbildung 1 kann missverstanden werden, wenn man die Funktion switch() nicht kennt: Die Konstrollstruktur if else realisiert die Alternative, dagegen sollte man switch() als eine Funktion auffassen, die lediglich eine vereinfachte Version der Mehrfachalternative ist. Was genau mit dieser Aussage gemeint ist, wird im Folgenden erkl├Ąrt.

Zun├Ąchst zur Alternative und if else: In Abbildung 1 links wird durch die Bedingungspr├╝fung festgestellt, welcher von zwei m├Âglichen F├Ąllen vorliegt, und anschlie├čend entweder Anweisung 2 oder Anweisung 3 ausgef├╝hrt. Dabei kann anstelle einer einzigen Anweisung auch ein ganzer Block von Anweisungen stehen. An der Syntax von if else erkennt man dies an den geschweiften Klammern, die mit beliebigen Anweisungen gef├╝llt werden k├Ânnen:

if(cond){
    # Anweisungen
} else {
    # Anweisungen
}

Dagegen sollte aus den Erl├Ąuterungen zur Funktion switch() klar geworden sein, dass mit switch() keine Bl├Âcke von Anweisungen angesteuert werden k├Ânnen. In anderen Programmiersprachen wird switch() etwa so implementiert, wie es das folgende Skript im Pseudocode andeuten soll:

switch(EXPR){
    case 1:
        # Block 1;
    case 2:
        # Block 2;
        
    # und so weiter
    
    default:
        # Block default
}

Hier wird ebenfalls ein Ausdruck EXPR gepr├╝ft, von dem hier angenommen wird, dass er ganze Zahlen 1, 2, ... liefert. Je nachdem, welche Zahl vorliegt, wird ein anderer Block von Operationen ausgef├╝hrt. Zuletzt wird sogar ein default-Block ausgef├╝hrt, der dann gew├Ąhlt wird, wenn keine der in case 1, case 2, ... aufgef├╝hrten Zahlen vorliegt.

Diese Komplexit├Ąt der Mehrfachalternative l├Ąsst sich durch switch() nicht unmittelbar realisieren. Oben wurde gezeigt, dass switch() immer nur einen Wert zur├╝ckgibt. Man kann nat├╝rlich diesen Wert verwenden, um die Ausf├╝hrung eines Blockes von Anweisungen anzusto├čen, etwa indem man sich eine geeignete Funktion zurechtlegt. Mit diesem zus├Ątzlichen Schritt kann man die komplette Mehrfachalternative realisieren. Hier wird dies nicht vorgestellt, da man dazu einiges Wissen ├╝ber selbst-definierte Funktionen ben├Âtigt.

Zusammenfassung

Die Funktion switch(EXPR, ...) gibt es in zwei Versionen:

  1. integer-Version
  2. character-Version.

Die Funktion switch(): integer-Version

Liefert EXPR die ganze Zahl i, so wird die i-te Komponente aus dem Argument ... ausgew├Ąhlt und von switch() zur├╝ckgegeben.

switch(EXPR, c1, c2, ..., cn)

F├╝r EXPR == i wird ci zur├╝ckgegeben.

Die Funktion switch(): character-Version

Liefert EXPR die Zeichenkette "x" , so wird aus dem Argument ... die Komponente mit Namen x ausgew├Ąhlt und von switch() zur├╝ckgegeben.

switch(EXPR, x = c1, y = c2, ...)

F├╝r EXPR == "x" wird c1 zur├╝ckgegeben.