"switch"-Anweisungen, komische Syntax und Pseudo-Python

Alles, was nicht direkt mit Python-Problemen zu tun hat. Dies ist auch der perfekte Platz für Jobangebote.
Antworten
Üpsilon
User
Beiträge: 222
Registriert: Samstag 15. September 2012, 19:23

Hallo Leute!

In vielen Programmiersprachen gibt es ja eine switch-Anweisung.

In Python gibt es die leider (noch?!) nicht, das finde ich ein bisschen schade, weil das "switchen" einem viel Tipperei erspart.

In C++ kann man es ja so machen:

Code: Alles auswählen

// ACHTUNG!! UNGETESTET!!

#include <iostream>
using namespace std;

int main {

    cout << "Wähle dein Lieblingseis \n";
    cout << "1 - Vanille \n";
    cout << "2 - Erdbeere \n";

    int gewaehlt;
    cout << "Deine Wahl: "
    cin >> gewaehlt

    switch(gewaehlt) {
        case 1:
            cout <<  "Vanille-Eis ist scheisse! \n"; break;
        case 2:
            cout << "Cool, ich mag Erdbeer-Eis auch :-) \n"; break;
        default:
            cout << "Ich notier mal: Aussage verweigert...";
    }

}

Und jetzt raffe ich eine Sache nicht: WIESO benutzt man nach dem case einen Doppelpunkt (wie in Py), und nicht, wie sonst immer, die Klammern { }

Und die zweite Sache: Ist switch für Python mal geplant?
BlackJack

@Üpsilon: Was sich Kernighan und Ritchie bei der C-Syntax gedacht haben, musst Du sie wahrscheinlich selber fragen.

Ich denke nicht das Python ein ``switch`` bekommen wird. Für viele Sachen kann man an der Stelle mit einem Wörterbuch arbeiten. Und ausserdem ist ein ``switch`` in bestimmten Situationen auch ein Hinweis, dass man an der Stelle besser OOP verwenden sollte statt etwas über einen Wert zu dispatchen.

In Deinem Beispiel hast Du Eissorten und jeweils einen Text dazu und verbindest die dann über den Umweg mit dem ``switch``. Das könnte man beispielsweise in Python mit einer Liste lösen.

Edit:

Code: Alles auswählen

#!/usr/bin/env python


def main():
    icecream_flavours = [
        ('Vanilla', 'Vanilla icecream sucks!'),
        ('Strawberry', 'Cool, I like strawberry icecream too :-)'),
    ]
    print 'Choose your favourite flavour'
    for i, (flavour, _) in enumerate(icecream_flavours, 1):
        print '{0} - {1}'.format(i, flavour)
    choice = int(raw_input('Your choice: ')) - 1
    if 0 <= choice < len(icecream_flavours):
        print icecream_flavours[choice][1]
    else:
        print 'Noted: Refused to give a statement.'


if __name__ == '__main__':
    main()
Sirius3
User
Beiträge: 17749
Registriert: Sonntag 21. Oktober 2012, 17:20

Netürlich wurde schon über ein »switch«-Statement nachgedacht: PEP3103. Die Probleme die man sich damit einhandelt sind aber größer als der Gewinn.
In C spekulier ich mal und vermute, dass »switch« Ähnlichkeiten mit »goto« hat und die Cases dann Labels wären.
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

@Sirius3: Genau. Deswegen geht in C auch als sowas:

Code: Alles auswählen

send(to, from, count)
register short *to, *from;
register count;
{
    register n = (count+7)/8;
    switch (count%8) {
        case 0: do {    *to = *from++;
        case 7:         *to = *from++;
        case 6:         *to = *from++;
        case 5:         *to = *from++;
        case 4:         *to = *from++;
        case 3:         *to = *from++;
        case 2:         *to = *from++;
        case 1:         *to = *from++;
        } while (--n>0);
    }
}
@Üpsilon: Sollte es irgendwann ein Switch-Statement in Python geben, werde ich mir eine andere Sprache suchen. Überproportional viele Neuerungen der letzten 20 Jahre bei der Entwicklung neuer Sprachen/Sprachkonzepte bestanden darin, dass man Möglichkeiten einführte, explizite Fallunterscheidungen loszuwerden. Polymorphismus/OOP wurde ja schon genannt. Ein anderes Beispiel wäre Ausnahmebehandlung. Gäbe es die nicht in Python müste man etwa sowas programmieren:

Code: Alles auswählen

some_result = some_function(x, y, z)
if some_result == IOERROR:
    ...
elif some_result == FOOERROR:
    ...
elif some_result == BARERROR:
    ...
...  # andere mögliche Fehler...
else:  # alles ok
    # mach irgendwas mit some_result
Dabei müssen jedesmal, wenn some_function() verwendet wird, alle potentiellen Fehler abgefragt werden, bevor man das Ergebnis weiterverwenden kann. Dieser Code ist noch in weiterer Hinsicht problematisch. Nicht nur muss an dieser Stelle jede mögliche Ausnahme behandelt werden, sondern auch an jeder anderen Stelle, wo some_function() aufgerufen wird. Sollte es nötig werden some_function() so zu erweitern, dass eine neue Ausnahme eintreten kann welche an den Aufrufer zurückgegeben werden muss, dann müssen alle diese Stellen angefasst und um einen Zweig erweitert werden. Das ist insbesondere dann sehr schlecht, wenn some_function() zu einer API gehört, die von Programmierern verwendet wird, die nicht zum eigenen Umfeld gehören. Stell dir vor, Microsoft würde irgendeine oft verwendete Windowsfunktion ändern, so dass Millionen von Zeilen von Code weltweit durchsucht werden müssen, um jede Stelle, an der diese Windowsfunktion verwendet wird, anzupassen. Nicht, dass so etwas noch nie passiert wäre, aber es ist jedesmal mit erheblichem Zeitaufwand, und damit Geldaufwand, verbunden.

Da Python einen Mechanismus zur Ausnahmebehandlung besitzt, kann man es dagegen so machen:

Code: Alles auswählen

try:
    some_result = some_function(x, y, z)
except IOEXCEPTION:
    ...
# mach irgendwas mit some_result
Sollte hier eine FOOEXCEPTION oder BAREXCEPTION auftreten, muss diese nicht im lokalen Code behandelt werden, sondern kann einfach an den Aufrufer des lokalen Codes weitergereicht werden. Der lokale Code darf also dümmer sein. Dadurch wird ermöglicht, dass some_function() geändert werden kann (indem eine neue mögliche Ausnahme eingefügt wird), ohne dass jeder Client Code angefasst und angepasst werden muss.
Zuletzt geändert von pillmuncher am Dienstag 15. Oktober 2013, 00:33, insgesamt 1-mal geändert.
In specifications, Murphy's Law supersedes Ohm's.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

In einigen statisch typisierten funktionalen Sprachen gibt es auch noch sowas wie Option Types (in Haskell dann Maybe), also quasi einen Rückgabewert der entweder das Ergebnis enthält oder einen Fehlerwert. Um das Ergebnis zu bekommen muss man Pattern Matching anwenden welches den Programmierer zwingt beide Fälle zu behandeln. Das schöne daran ist jetzt, dass man diese Option-Types durchreichen kann und erst ganz am Ende der Berechnung die aus mehreren Schritten bestehen kann explizit Pattern-Matchen muss. Damit umgeht man Exception Handling was sich in deklarativen Sprachen IMHO etwas wie ein Fremdkörper anfühlt und vermeidet trotzdem Fehler zu übersehen wie das in C-Code leicht passieren kann.

Hmm, Pattern Matching ist so ein wenig wie Switch auf Steroiden :)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

pillmuncher hat geschrieben:Überproportional viele Neuerungen der letzten 20 Jahre bei der Entwicklung neuer Sprachen/Sprachkonzepte bestanden darin, dass man Möglichkeiten einführte, explizite Fallunterscheidungen loszuwerden.
Es ist zwar etwas aelter aber Pattern Matching gehoert auch dazu und das ist eigtl auch die Art von Switch-Statement die man haben will. Der Bonus des Dispatch durch ein dict ist dann auch, dass man zu Funktionen gezwungen wird und der Code staerker gekapselt und modularisiert werden muss, kurzum: Man muss nachdenken.

Edit:
Leonidas hat geschrieben:Hmm, Pattern Matching ist so ein wenig wie Switch auf Steroiden :)
Und dann schlaegst du mich auch noch um 2 Minuten ...
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Bild
In specifications, Murphy's Law supersedes Ohm's.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Goto fehlt auch. PHP hat das letztens eingeführt, vielleicht bekommen wir das auch bald ;)
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
BlackJack

@Leonidas: Gibt's doch schon als Modul (http://entrian.com/goto/). Viel interessanter ist die Frage wann PHP endlich ein ``comefrom`` bekommt, damit Python mit dem Modul nicht mehr im Vorteil ist. :twisted:
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

In der Tat übersetzt der GCC die Switch-Anweisung nahezu identisch zu:

Code: Alles auswählen

if (a==1) goto label1;
if (a==2) goto label2;
goto label_default;
label1:
  ...;
  goto label_end; /* analog zu break */
label2:
  ...;
label_default:...;
label_end:
Zumindest auf dem x86 ist die switch-Anweisung sehr hardwarenah an "cmp" und "jmp".

@Üpsilon: Daher wären geschweifte Klammern innerhalb der "cases" problematisch, da diese eben keine getrennten Blöcke sind, sondern nur Sprungmarken innerhalb eines Blockes, wie man ohne "break" gut nachvollziehen kann.
BlackJack

@jerch: Aber man kann doch Blöcke in die einzelnen ``case``\s setzen, also Blöcke mit Labeln.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@BlackJack: Natürlich geht das, war auch nicht, was ich meinte. Vielmehr sind die "Case-Grenzen" keine Blockgrenzen, deshalb geht ja das, was Duff da "mißbräuchlich" ausnutzte.

Würde man geschweifte Klammern nutzen wollen und deren üblichen Sinn nicht entstellen, müßte man eher sowas schreiben:

Code: Alles auswählen

switch (a) {
  case (1) {
    ...;
    case (2) {
    ...;
    }
  }
}
da nachfolgende statements erreichbar sind, ohne dass die nachfolgenden "case-Bedingungen" nochmal geprüft werden (oben nicht wirklich klar). Lesbarer wirds damit keinesfalls, die Doppelpunkt-Notation ohne Klammern bildet diesen Umstand mMn besser ab. (Ein wirkliches Äquivalent in konsistenter Klammernotation gibt es nicht, mit den Klammern holt man sich viele Probleme ins Boot: Wie sieht das switch case2, da es doch nested ist? Wo landet man nach break? Braucht man ein "super-break"? Welchen scope haben die unteren Klammerabschlüsse?)

Oder man führt halt exklusive Blöcke ein wie in Pascals "case .. of". Dann könnte man das switch konsistenter abbilden:

Code: Alles auswählen

switch (a) {
  case (1) {
    ...;
  }
  case (2) {
    ...;
  }
  ...
}
Edit: Ok, letztere Notation könnte man für das C-Verhalten nutzen, wenn man die Bedingungen ODER-verknüpft denkt:

Code: Alles auswählen

switch (a) {
  orcase (1) {
    ...;
  }
  orcase (2) {
    ...;
  }
  ...
}
Der erste Treffer würde nachfolgende orcase-Blöcke einschliessen :D
Antworten