Donnerstag, Mai 07, 2009

RewriteParser & RewriteParser-Kombinatoren

In der Vergangenheit habe ich über "Objekt-orientierte Parser-Kombinatoren in Python" geschrieben -- ein Thema, das mich immer noch fasziniert. Es ist so unglaublich einfach, einen Parser mit Parser-Kombinatoren zu entwickeln. Das ist gepaart mit einer Flexibilität, die traditionelle Parser-Ansätze a la lex & yacc nicht bieten.

In diesem Beitrag geht es (i) um die Verallgemeinerung eines Text-Parsers zum Parsen beliebiger Objektfolgen mit Hilfe zweier Stacks und (ii) um Parser, die ich RewriteParser nenne, und deren Kombination mit einem ganz speziellen RewriteParser-Kombinator, RPC*. Ich habe auf eine konkrete Programmiersprache verzichtet, damit das Prinzip klarer zutage tritt.

Um die Idee der Parser-Kombinatoren nicht auf das Parsen von Zeichenketten zu beschränken, verallgemeinern wir die Idee, parsen beliebige Objekte von einem Stack und legen die Ergebnisse auf einem anderen Stack ab. Ein Parser (und somit auch ein Parser-Kombinator) nimmt zwei Stacks entgegen, ich nenne sie inStack und outStack, und liefert neben einem Flag für erfolgreiches Parsen zwei Stacks zurück, inStack' und outStack'. Zur Info: meine Stacks sind funktional, sprich immutabel.

Was macht ein Parser generell? (1) Er prüft, ob ein bestimmtes Muster auf dem inStack zu finden ist -- dafür gibt es eine Funktion Crit(erion):Stack->Bool. (2) Er konsumiert kein oder mehr Elemente vom inStack -- die konsumierende Funktion heiße Consume:Stack->Stack. (3) Er produziert kein oder mehr Elemente für den Ausgabe-Stack -- der produzierte Stackanteil werde über Produce:Stack->Stack erzeugt.

Wir können das etwas formaler beschreiben mit P(arser), Consume und Produce als Funktionen und SUCCESS = True, FAILURE = False. Der '+'-Operator produziert einen neuen Stack aus dem Aneinanderhängen zweier gegebener Stacks.

P(Crit,Consume,Produce)(inStack,outStack) --> success,inStack',outStack'
if !Crit(inStack): return FAILURE, inStack, outStack
return SUCCESS, inStack', outStack'
with inStack' + Consume(inStack) == inStack
and outStack' == outStack + Produce(inStack)

Die "with"-Schreibweise ist vielleicht etwas ungewöhnlich, aber sie besagt nur, dass der inStack' ein um Consume(inStack) reduzierter inStack ist. Und dass der outStack immer nur um Produce(inStack) anwachsen kann zu outStack'. Ich nutze für die Stackreduktion weiter unten kurzfristig auch das "-":

inStack' == inStack - Consume(inStack)

Ein Parser-Kombinator, der ein oder mehr Parser in irgendeiner Weise verknüft (bisweilen ist die Verknüpfung konfigurierbar), muss sich an dieselben Spielregeln halten: er ist auch wieder ein Parser, der möglicherweise Teile vom inStack oder den ganzen inStack konsumiert und möglicherweise etwas zum outStack beiträgt. Beachten Sie nochmal, dass Konsum und Produktion ausschließlich eine Funktion vom inStack sind! Der outStack kann z.B. als Gedächtnis nicht genutzt werden!

Nachfolgend beschreibe ich einen Parser-Kombinator der zwei Parser komponiert: PC kombiniert zwei Parser P1 = P(Crit1,Consume1,Produce1) und P2 = P(Crit2,Consume2,Produce2):

PC(P1,P2)(inStack, outStack) --> success, _inStack, _outStack
success', inStack', outStack' = P1(inStack, outStack)
if !success': return FAILURE, inStack, outStack
success'', inStack'', outStack'' = P2(inStack', outStack')
if !success'': return FAILURE, inStack, outStack
return SUCCESS, inStack'', outStack''

Man könnte auch für den Erfolgsfall zusammenfassen:

inStack'' == inStack' - Consume2(inStack') <==>
inStack'' == inStack - Consume1(inStack) - Consume2(inStack - Consume1(inStack))

und

outStack'' == outStack + Produce1(inStack) + Produce2(inStack)

Die Komposition zweier Parser tut genau das, was man erwartet: erst konsumiert der erste Parser etwas vom inStack und legt sein Ergebnis auf dem outStack ab, dann folgt der zweite Parser, der weiteres vom inStack konsumiert und zusätzlich etwas auf dem outStack ablegt.

Nachfolgende möchte ich eine Parser-Klasse vorstellen, die ich RewriteParser nenne. Sie verallgemeinern das Parser-Prinzip; sie sind aus meinen Spielereien und der Suche nach immer wieder neuen, verbesserten, generischeren Parser-Kombinatoren hervorgegangen. Allerdings hat es eine Weile gedauert, bis ich ihre neue Eigenschaft destilliert hatte.

Der Unterschied zum Parser ist: Der Rewrite-Parser (RP) kann den inStack modifizieren! Der outStack kann nachwievor nicht gelesen, sondern nur produzierend (d.h. mit der Ablage neuer Elemente) bedient werden.

RP(Crit,Rewrite,Produce)(inStack,outStack) --> success,inStack',outStack'
if !Crit(inStack): return FAILURE, inStack, outStack
return SUCCESS, inStack', outStack'
with inStack' == Rewrite(inStack)
and outStack' == outStack + Produce(inStack)

Die Modifikation ist gering. Rewrite ist ebenso wie Consume eine Funktion, Rewrite:stack->stack. Sie konsumiert jedoch nicht nur einen Teil des inStack, sondern kann ihn vollständig umschreiben und beispielsweise neue Elemente auf dem inStack ablegen (z.B. als Gedächtnis etwa in Form von Annotationen). Der nachfolgende RewriteParser arbeitet dann mit diesem modifizierten inStack.

Schauen wir uns jetzt einmal einen RewriteParser-Kombinator an, der zwei Parser in eine Kompositionsbeziehung miteinander bringt. Wir berücksichtigen nun wieder den outStack. Es gilt RP1 = RP(Crit1,Rewrite1,Produce1), RP2 = RP(Crit2,Rewrite2,Produce2).

RPC(RP1,RP2)(inStack,outStack) --> success, _inStack, _outStack
success', inStack', outStack' = RP1(inStack,outStack)
if !success': return FAILURE, inStack, outStack
success'', inStack'', outStack'' = RP2(inStack',outStack')
if !success'': return FAILURE, inStack, outStack
return SUCCESS, inStack'', outStack''

Um das Kompositionsverhalten im Erfolgsfall verstehbar zu machen, auch hier wieder die Auflösung:

inStack'' == Rewrite2(inStack') <==> (mit inStack' == Rewrite1(inStack))
inStack'' == Rewrite2(Rewrite1(inStack))

Voila! Die Komposition zweier RewriteParser ist die Komposition ihrer Rewrite-Funktionen angewendet auf den inStack!

outStack'' == outStack' + Produce2(inStack')
outStack' == outStack + Produce1(inStack)

mit

inStack' == Rewrite1(inStack)

ergibt sich

outStack'' == outStack + Produce1(inStack) + Produce2(Rewrite1(inStack))

Das Ergebnis für den OutStack überrascht nicht. Hier legen RP1 und RP2 ihre Ergebnise der Verarbeitung von inStack bzw. inStack' ab

Wir können schon einmal festhalten: Die Kombination von RewriteParsern entspricht der Funktionskomposition plus Seiteneffekte!

Es gibt einen kleinen Kniff, der die Sache nun vollends interessant macht. Berücksichtigen wir bei der Komposition doch den Output von RP1, outStack', beim Input von RP2. Damit ist es nicht mehr nötig, RP2 als Ausgabe-Stack outStack' zu geben, da er den prinzipiell bei Produce berücksichtigen kann, sondern outStack. Ebenso benötigt RP1 nun nur einen EMPTYSTACK.

Diesen modifizierten RPC bezeichne ich RPC*.

RPC*(RP1,RP2)(inStack,outStack) --> success, _inStack, _outStack
success', inStack', outStack' = RP1(inStack,EMPTYSTACK)
if !success': return FAILURE, inStack, outStack
success'', inStack'', outStack'' = RP2(inStack'+outStack',outStack)
if !success'': return FAILURE, inStack, outStack
return SUCCESS, inStack'', outStack''

Es ergibt sich nun für inStack'':

inStack'' == Rewrite2(inStack'+outStack')
inStack' == Rewrite1(inStack)
outStack' == EMPTYSTACK + Produce1(inStack)
---
inStack'' == Rewrite2(Rewrite1(inStack)+Produce1(inStack))

Und für outStack'' ergibt sich:

outStack'' == outStack + Produce2(inStack'+outStack')
outStack'' == outStack + Produce2(Rewrite1(inStack)+Produce1(inStack))

Das Ergebnis hat zwei bemerkenswerte Eigenschaften. Zum einen: Darin ist vollständig der Parser-Kombinator RPC nachbildbar über geeignete Rewrite- und Produce-Funktionen. RPC* ist also erheblich mächtiger. Zum anderen: Es kommt wunderbar aus, dass das Argument zu Rewrite2 in inStack'' und zu Produce2 in outStack'' das gleiche ist! Das Ergebnis inStack'' kann entweder auf "normale" Funktionskomposition reduziert werden oder es bekommt ein Ergebnis einer Vorverarbeitung durch Produce1(inStack) dazu! Mit diesem Input arbeitet auch Produce2 und kann das Gedächtnis beim Hinzufügen eines Ergebnisses zu outStack berücksichtigen.

Für diejenigen, die sich mit konkatenativer Programmierung schon einmal befasst haben (siehe z.B. Factor, Joy oder Cat), sei verraten, dass sich das konkatenative Paradigma mit RewriteParsern und RPC* vollständig umsetzen lässt. Oder anders herum: Konkatenative Programmierung kann man verstehen als die Programmierung mit RewriteParsern, die durch PRC* komponiert werden.

Ich weiß, das war viel. Aber vielleicht werden Sie den Nutzen der RewriteParser erkennen, wenn Sie selber einmal mit Parser-Kombinatoren arbeiten und programmieren.

Montag, März 30, 2009

Abwrackprämie am Limit

(Update 31. März 2009: Der Geschäftsführer der Babiel GmbH, Herr Dr. Rainer Babiel, hat mich gebeten noch einmal ausdrücklich darauf hinzuweisen, dass seine Firma nicht für das BAFA-Webformular rund um die Abwrackprämie verantwortlich ist. Das betrifft auch das Hosting. Dies geht auch aus den Nachträgen zu diesem Blogbeitrag hervor.)

Ich gehöre auch zu denen, die sich heute morgen mit einer Funkuhr bewaffnet vor den Rechner setzten, um am Wettrennen um die Reservierung der "Abwrackprämie" teilzunehmen. Der Wettstreit wurde um 8 Uhr vom Bundesamt für Wirtschaft und Ausfuhrkontrolle eröffnet unter www.ump.bafa.de.

Bereits vor einer Woche habe ich mich schon zusammen mit einem Freund über den vorherzusehenden Zusammenbruch aufgeregt. Denn was die Sache so heikel macht: Jeder über die Webseite gestellte Antrag wird mit einer Zeitmarke versehen. Wer zuerst auf dem Webserver der BAFA registriert wird, der wird vorrangig bei der Gewährung der Umweltprämie für Altfahrzeuge behandelt. Bekanntlich ist der Geldtopf für die Prämie limitiert.

Um 8 Uhr lief rein gar nichts. Nicht einmal ein "ping" ging an den Webserver durch. Um 8:14 Uhr bekam ich einen ersten "Sneak Preview" auf das Formular. Ein nettes Gimmick hat man sich da ausgedacht: Mit der Auswahl des Fahrzeugherstellers beginnt die Webseite irgendetwas nachzuladen, ich vermute den Fahrzeugtyp -- und kriegt natürlich keinen Kontakt mehr zum Server, da der überlastet ist.

Ich rufe bei der BAFA an und melde meinen Protest an. Natürlich sitzt dort eine ebenso überlastete Dame im CallCenter. Über 90% der Anrufe seien Beschwerden zum Thema, sagt sie. Über das Impressum der BAFA finde ich heraus, wer für den Internet-Auftritt verantwortlich und technischer Provider ist. Ich rufe dort um 10:05 Uhr an. Die Dame der Babiel GmbH kann mir lediglich verraten, dass man um das Problem des überlasteten Webauftritts wisse und man an seiner Behebung arbeite. Eine alternative IP-Adresse habe sie nicht. Und wann man den Server wieder im Griff habe, könne sie auch nicht sagen.

Tja. Ich bin fassungslos -- und verärgert. Wie kann man so naiv sein? Nach diesem medialen und politischen Getöse um die Abwrackprämie ist ja wohl nicht schwer vorherzusehen gewesen, dass heute morgen Autohäuser, Privatmenschen und viele Journalisten für unzählige HTTP-Requests auf www.ump.bafa.de sorgen würden. 2.500 Euro sind ein mächtiger Anreiz für den Besuch der Webseite. Und wie kann man ein Formular entwerfen, das die Serverlast mit Nachladerei noch erhöht und den Frust aller Reservierungswilligen nur steigert?

Die BAFA stellt Regelungen auf, deren technische Umsetzung die von ihr beauftragte Firma nicht zu beherrschen scheint. Damit ist die Reservierung der Umweltprämie kein faires Verfahren mehr. Es ist keine Sache der Schnelligkeit mehr sondern ein Glücksspiel, wer hier wann zuerst seinen Antrag elektronisch einreichen kann. Das wird eine Welle des Protests erzeugen. Die BAFA kann als Lösung eigentlich nur noch die Reihenfolge der heutigen Reservierungen per Losentscheid erstellen. Fraglich bleibt, ob man seinen Antrag heute überhaupt noch per Webformular stellen kann. Mittlerweile ist es fast 11 Uhr. Über drei Stunden hat niemand das Problem lösen können.


Nachtrag 0:39 Uhr, 31. März: Unfassbar! Ein letzter Versuch vor dem Weg ins Bett beschert mir unverhofft ein Erfolgserlebnis: Der Antrag ist durch! Nach etwas mehr als 16 Stunden! Die zugeteilte Vorgangsnummer liegt über einer Million. Was mir das sagen soll, weiß ich nicht. Hoffentlich nichts Ungutes.

Nachtrag 22:52 Uhr: Die Webseite zur Reservierung der Umweltprämie scheint gänzlich vom Netz zu sein. In einer Pressemitteilung bedauert das BAFA "technische Schwierigkeiten" und verkündet "externer Dienstleister arbeitet an Lösung". Wir dürfen gespannt sein, was es mit den in der Pressemitteilung erwähnten Denial-of-Service-Attacken auf sich haben soll -- das BAFA will morgen um 10 Uhr weitere Hinweise auf seinen Webseiten veröffentlichen. Ich bin gespannt, ob sich der BAFA-Datenschutzbeauftragte Ulrich Schydlo (siehe BAFA-Impressum) morgen aufgeweckt zeigt. Allein die unverschlüsselten Formulareingaben hätte er wohl kaum tolerieren dürfen. Sollten die heise-Meldungen stimmen, dann hätte er allen Grund vehement einzuschreiten.

Nachtrag 18:58 Uhr: Auf heise.de ist vor einer halben Stunde von massiven Pannen in Sachen Datenschutz berichtet worden: "Auto-Abwrackprämie: Daten(schutz)pannen bei Online-Reservierung". Die Bestätigungsmails werden angeblich nicht immer an die richtigen Antragsteller(innen) geschickt. Ich bin nicht einen Schritt weiter gekommen. Im Moment sieht es nicht besser als kurz nach 8 Uhr heute morgen aus.

Nachtrag 17:04 Uhr: Die Reaktionszeiten des Portals haben sich spürbar verbessert. Nun kann ich wiederholt meine Daten angeben, um dann dabei unterbrochen zu werden mit dem Hinweis:

Willkommen auf dem Antragsportal für die Umweltprämie!

Leider ist das Antragsportal zur Zeit überlastet. Bitte versuchen Sie es in einigen Minuten erneut. Vielen Dank für Ihr Verständnis.

Auf mein Verständnis sollte man nach einem Tag nicht mehr hoffen!

Nachtrag 15:05 Uhr: Immerhin, ich bin bis zur Gotcha-Seite vorgedrungen. Ich klicke auf weiter und komme zur einer Seite mit dem wörtlichen Hinweis:

Aktuell laden sehr viele Antragsteller einen Kaufvertrag hoch.

Die Anzahl paralleler Uploads ist aber begrenzt. Diese Seite aktualisiert sich automatisch, bis Sie Ihren Upload starten können.
Verzichten Sie bitte daher auf die Funktion 'Neu laden'

Die Wartezeit sollte 60 Sekunden nich überschreiten.

Ja, da steht "nich überschreiten". Übrigens muss man als Vollendung dieser Unprofessionalität den Versand der eigenen Daten über eine unsichere Verbindung hinnehmen! Das ist schon fast ein regelrechter Datenskandal!

Nachtrag 12:58 Uhr: Im Impressum der BAFA ist mittlerweile ein Hinweis erschienen, dass die Formularseite für die Reservierung der Umweltprämie nicht von der Babiel GmbH gehostet wird. Als ich dort gegen 10 Uhr anrief, wusste die Firma selbst das noch nicht ...

Nachtrag 11:53 Uhr: Die folgende Meldung findet sich auf der Homepage der BAFA -- auch das hat Stunden gedauert:

Aufgrund von technischen Problemen kommt es derzeit zu Schwierigkeiten beim Aufruf und beim Ausfüllen des Reservierungsantrags, die nicht im Einflussbereich des BAFA liegen. Das BAFA unternimmt alles in seiner Macht stehende, damit dieses Problem schnellstmöglich behoben wird. Bis dahin bitten wir um Ihr Verständnis.

Die Meldung an sich ist bereits amüsant. Die technischen Probleme liegen nicht im Einflussbereich der BAFA. Dennoch will man Mächte aktivieren, die Einfluss auf den Einflussbereich nehmen. Spannend!

Sonntag, März 01, 2009

Konkatenative Programmiersprachen

Mit dem heutigen Tag beginnt offiziell mein Forschungssemester. Mein Thema: "A Model of Computation in Space and Time" -- darüber werde ich vielleicht ein anderes Mal was schreiben.

Was sich in den letzten Wochen mehr und mehr aufdrängt, ist, das ein Schlüssel zu meinem Thema in den konkatenativen Sprachen liegt. Ich weiß es noch nicht sicher, aber mit jedem Tag finde ich mehr und mehr Gründe, mich mit dieser Klasse von Sprachen auseinander zu setzen.

Was, Sie haben noch nie etwas von concatenative languages gehört?

Verwunderlich ist das nicht. Konkatenative Sprachen führen zu unrecht ein vollkommenes Schattendasein. Wenn Sie sich für Ruby, Python, Groovy, Scheme, Lisp, Clojure, Haskell, F#, Prolog begeistern können, dann sollten konkatenative Sprachen wie Joy, Cat und Factor ein gefundenes Fressen für Sie sein. Diese drei Sprachen führen Sie in eine ganz neue Programmierwelt ein.

Konkatenative Sprachen gehören zu den funktionalen Sprachen. Manfred von Thun hat auf seiner Webseite zu Joy einen wunderbaren Satz an Beiträgen zusammengestellt, die behutsam in diese rein funktionale Welt konkatenativer Programmierung mit Joy einführen. Auch die Sprache Cat von Christopher Diggins ist interessant. Cat ist in C# implementiert und ein Studium wert.

Während Joy und Cat mehr Experimentalcharakter haben, ist Factor eine ernstzunehmende Programmiersprache, die der Pragmatik zuliebe auf ein striktes funktionales Paradigma verzichtet. Für mich ist Factor die spannendste Sprache, die am Programmierhorizont zu sichten ist. Ich habe Factor schon ein Weilchen auf dem Radar und schrieb vor einem Jahr "Factor: In der Kürze liegt die Würze". Ihr Schöpfer, Slava Pestov (übrigens hat er auch jEdit entwickelt), wird übermorgen, am 3. März 2009, 25 Jahre alt! Vielleicht mag das insbesondere die Informatik-Studierenden unter meinen Lesern motivieren. Andererseits: Slava Pestovs Biographie liest sich fast wie die eines Wunderkindes, der früh einen Draht zur Mathematik und Informatik fand. Seine Sprache Factor ist so voll gefüllt mit modernen Programmierkonzepten und so intelligent aufgebaut -- ich finde es mehr als beachtlich, wie man so jung derart smart und zielstrebig eine so qualitativ hochwertige Programmiersprache auf die Beine stellen kann, die einen Guido van Rossum (Schöpfer von Python) oder einen Matz (Yukihiro Matsumoto, Schöpfer von Ruby) alt aussehen lassen.

Vielleicht habe ich Sie jetzt ja neugierig gemacht ...

Samstag, Februar 28, 2009

Ist der Kassen-Bug von Lidl ein Bug?

Mein Posting "Lidl und der Kassen-Bug" ist seit seinem Erscheinen immer noch der am meisten gelesene Beitrag in meinen Denkspuren. Nie hätte ich daran geglaubt, dass er diese Popularität genießen würde, denn er ist einer von den Beiträgen, die mir selber weniger bedeuten. "Lidl und der Kassen-Bug" erschien am 15. April 2008. Ich hatte den Tipp von einem meiner Studierenden bekommen. Allerdings wollte ich mit eigenen Augen sehen, ob der Tipp wirklich das versprochene Ereignis auslöst, bevor ich darüber blogge.

Als ich über den Kassen-Bug berichte, gibt es einen kleinen, fast schon aufregenden Peak in den Visits (ich nutze Google Analytics). Die seinerzeit noch üblichen 20, 30, 40 oder auch mal 50 Visits an einem Tag schrauben sich am 15. April auf ungekannte 97 Visits hoch. Am Folgetag gibt es 190 Visits zu verzeichnen, dann 124, dann 100, ... und dann ist schon wieder alles beim alten. Ein paar Tage später, mit Beginn der neuen Woche beginnt es zu rumpeln. Genau eine Woche später, am 22. April scheint der Beitrag auf das Radar von Lesern zu kommen, die sonst die Denkspuren nicht besuchen. Am 4. Mai verzeichne ich den absoluten Besuchsrekord: 5.362 Visits an einem Tag! Es ist ein Peak, aber in der Folgezeit habe ich immer noch ungewohnt hohe und teils stark schwankende Besuchszahlen. Allein in der Zeit vom 22. April bis zum 21. Mai 2008 zählt Google Analytics 25.150 Visits und 34.586 Pageviews. Anfang Juni ist der "Spuk" vorbei. Die Besuchszahlen pendeln sich allmählich wieder auf die Ausgangsdimensionen ein. Ich darf seitdem lediglich im Mittel eine etwas höhere Besuchszahl verzeichnen, meist in der Spannbreite von 35 bis 70 Visits/Tag. In den letzten 30 Tagen (29. Januar - 27. Februar) gab es 1.608 Visits und 1.987 Pageviews.

Das Ganze war für mich eine absolut interessante Lehrstunde in Sachen Dynamik, die das Web entwickeln kann! Beliebte Seiten, wie golem.de, hatten über "Lidl und der Kassen-Bug" berichtet und einen Teil des Ansturms ausgelöst. Ich hätte diese Entwicklung niemals für diesen Beitrag vorauszusagen gewagt. Never ever!

In den Kommentaren zu dem Posting (in meinem Blog und anderswo) spekulieren einige meiner Leser(innen) sehr phantasiereich über die Umstände der möglichen Entstehung des Kassen-Bugs. Einige haben sogar konkrete Codezeilen vor Augen. Nur eine sehr kleine Fraktion hält das von mir als Bug deklarierte Ereignis gar nicht für einen Fehler. Kann es sein, dass der Kassen-Bug bei Lidl gar kein Bug, sondern ein Feature ist?

Gut möglich. Dafür spricht, dass die Kasse bei dem Null-Betrag weder abstürzt noch abraucht (das wäre ein Spaß, was?! ;-), sondern ein definiertes Verhalten zeigt. Dagegen spricht, dass die Kassierer und Kassiererinnen auf dieses Kassenverhalten gar nicht vorbereitet sind. Sie reagieren ratlos, weil sie den Fehler nicht kennen, oder lösen das Problem mit einem Workaround ("Ich zieh das hier wieder ab, dann geht die Kasse wieder und sie zahlen das hier dann gesondert, ok?"). Was für ein Interesse sollte ein Discounter daran haben, bei einer Nullsumme die Kasse zu blockieren, wenn der Workaround so simpel ist?

Ist der Kassen-Bug nun ein Bug oder ein Feature?

Ich plädiere zwar auf Bug, glaube die Argumente auf meiner Seite, aber das will nichts heißen. Meinen kann man im Web viel. Letztlich ist die Frage nicht zu beantworten. Es sei denn, wir verlassen die Komfortzone des Browserfensters, begeben uns in die Welt da draußen und klären das Phänomen des Kassen-Bugs auf.

Wollen Sie wissen, ob der Kassen-Bug ein Bug ist? Wollen Sie mir das Gegenteil beweisen?

Finden Sie's raus! Besuchen Sie den nächsten Lidl und fragen Sie bei der Filialleitung nach. Vielleicht kommen Sie da weiter. Oder greifen Sie zum Telefon, rufen bei Lidl an und bitten um Kontakt mit dem "Kassen-Verantwortlichen"; man könnte es bei der Lidl-eigenen IT-Abteilung versuchen. Wer ist der Hersteller der Kassen? Kann man Ihnen dort in der Entwicklungsabteilung einen Hinweis geben oder das Problem aufklären? Irgendwo da draußen in der Realwelt gibt es jemanden, der uns die Frage "Bug oder Feature?" beantworten kann.

Der Erste bzw. die Erste, der/die mir bis zum 31. März 2009 eine EMail schickt mit einer nachvollziehbaren Antwort unter Quellenangaben wie z.B. Gesprächspartner samt Telefonnummer (ich möchte Ihre Angaben überprüfen können), dem verspreche ich ein kleines Buchgeschenk. Da wir das Darwin-Jahr haben, habe ich für Sie das Buch von Joachim Bauer "Das kooperative Gen" ausgesucht. Sie dürfen sich bei akzeptierter Antwort aber auch gerne ein anderes Buch bis 20 Euro wünschen.

Viel Erfolg!

[Nachtrag vom 10. März: Meinen herzlichen Glückwunsch an Marius Heisler. Dank seiner Recherchen weiß ich nun, dass der Kassen-Bug bei Lidl definitiv kein Feature ist. Heute hat mir die Pressestelle von Lidl das bestätigt. Die Pressesprecherin spricht selber von einer Kasse, "die sich aufgehängt hat". Sie bittet jedoch um Verständnis, "dass wir darüber hinaus keine detaillierten Angaben zur Softwarethematik machen möchten". Schade eigentlich.]

Montag, Januar 19, 2009

Was sind das für Zustände?


Zustandsmaschinen dienen in der Informatik zur Modellierung von Verhalten. Sie werden besonders gerne zur Spezifikation technischer Systeme eingesetzt. Oben im Bild sehen Sie ein einfaches Zustandsdiagramm. Zustandsdiagramme sind eine verbreitete Notation zur Darstellung von Zustandsmaschinen. Die Notation ist in der Unified Modeling Language (UML) standardisiert.

In dem Beispiel geht es um einen Roboterarm. Der Roboterarm ist entweder in dem Zustand "idle", "moving up/down" bzw. "welding" (schweißen); Zustände werden durch Rechtecke mit runden Ecken abgebildet. Der Start- und der Endzustand sind durch besondere Symbole hervorgehoben: durch einen Kreis (Start) bzw. einen Punkt im Kreis (Ende).

Die Pfeile beschreiben Übergänge (Transitionen) von einem Zustand in einen anderen und zwar abhängig von einem Ereignis. Die Ereignisse sind hier durch Buchstaben abgekürzt und stehen immer links vom Schrägstrich "/". Es sind Ereignisse, die der Roboter über seine Sensoren wahrnimmt. Die meisten der Ereignisse werden durch Steuerungssensoren wahrgenommen. Wenn ein Anwender den "down"-Knopf drückt, dann löst er damit das "d"-Ereignis aus. Ein "up" führt zu einem "u"-Ereignis, ein "s" zu "stop" und ein "o" ist die Folge eines "on"- bzw. "off"-Signals. Einzig das "c"-Ereignis wird durch einen Kontakt-Sensor ("contact") im Roboterarm ausgelöst. Dann "fühlt" der Roboterarm einen Widerstand.

Zu jedem Ereignis ist rechts vom Schrägstrich angegeben, was der Roboterarm für eine Aktion folgen lässt. Grundsätzlich möglich sind die Aktionen "move down", "move up", "stop moving", "weld" (schweiße), "initialize" und "shut down".

Befindet sich der Roboterarm im Zustand "idle" und tritt das Ereignis "d" auf (sprich, der Anwender hat den "down"-Knopf gedrückt"), dann bewegt sich der Roboterarm nach unten. Der Zustand wechselt von "idle" auf "moving down". Weitere "d"-Ereignisse bleiben ohne Folge, da sich der Roboterarm bereits in der Abwärtsbewegung befindet. Im Zustand "moving down" bewegt ein "u" den Arm wieder nach oben, ein "c" stößt die Schweiß-Aktivität an. Beim "u"-Ereignis wechselt der Zustand zu "moving up", beim "c"-Ereignis zu "welding".

Eine solche Zustandsmaschine kann man systematisch in Code übertragen. Der Vorgang ist derart schematisch, dass man ihn leicht automatisieren kann. Dafür übertragen wir in einem ersten Schritt das Diagramm in ein maschinenlesbares Format. Ich habe mich für XML entschieden. (Hinweis: Für die nachstehenden Code-Auszüge verwende ich den syntaxhighlighter. Möglicherweise gibt es Darstellungsprobleme, wenn Sie z.B. einen RSS-Reader nutzen. Dann besuchen Sie einfach direkt die Blogseite zum Posting.)



Das Eingangstag <fsm> steht für finite state maschine. Es folgt die Liste der (endlich vielen) Zustände, wobei der Start- und der Endzustand beliebig benannt werden können, sie dafür aber mit Hilfe des type-Attributes auszuweisen sind. Die Liste der Transitionen ist nahezu selbsterklärend. Die Aktionen geben wir als Programmcode in der Sprache C an. Das ist in dem Beispiel die Sprache, in der wir den Roboterarm programmieren, was nicht untypisch für Steuergeräte oder eingebettete Systeme ist. Zu Demonstrationszwecken ersetzen einfache printf-Statements "echte" Aktionen.

Das Interface des zu generierenden Codes -- Funktionsprototypen in C -- sieht wie folgt aus. Es ist absichtlich so einfach gehalten.


char * get_initState(void);
char * get_finalState(void);
char * get_toState(char *fromState, char *event);
void do_action(char *fromState, char *event);


Es ist einfach zu erraten, wie der generierte Code zu "get_initState" und "get_finalState" aussieht:


char * get_initState(void) { return "start"; }
char * get_finalState(void) { return "end"; }


Die Funktion "get_toState" liefert auf Angabe von "fromState" und "event" den dazugehörigen "toState" zurück. Ein Beispiel:


char * get_toState(char *fromState, char *event) {
if (strcmp("idle",fromState) == 0 && strcmp("o",event) == 0)
return "end";
}


Ein Kinderspiel, nicht wahr? Die Funktion "do_action" arbeitet ganz ähnlich, sie bettet im if-Rumpf den Aktionscode zur Transition ein:


void do_action(char *fromState, char *event) {
if (strcmp("idle",fromState) == 0 && strcmp("o",event) == 0) {
printf(">>> shut down\n");
}


Wenn wir einen Code-Generator haben, der die XML-Darstellung in diese Code-Schemata umwandelt, dann benötigen wir nur noch ein Abarbeitungsprogramm, das Steuerprogramm im Roboterarm, das auf Ereignisse wartet (Tastendrücke in unserem Beispiel), gemäß Zustandsmaschine die entsprechenden Aktionen ausführt und in einen neuen Zustand wechselt oder den bisherigen beibehält. (Die Funktion _getch() ist übrigens eine nicht standardgemäße Bibliotheksfunktion aus Pelles C zum Lesen eines Zeichens von der Tastatur.)



Wie aus dem Code ersichtlich ist, ignoriert die Steuerung nicht etwa Ereignisse für die keine Transition in einem Zustand definiert ist, sondern aktiviert eine Safety-Routine. Das ist ein durchaus sinnvolles Vorgehen bei Steuerungsaufgaben, die im Fehlerfall schwerwiegende Folgen nach sich ziehen können.

Stellt sich nur noch die Frage, wie der Code-Generator aussieht. Ich habe ihn in Python programmiert. Das Programm ist nicht sonderlich elegant, erfüllt aber zur Anschauung seinen Zweck.



Wenn Sie mögen, können Sie nun mit der Ergänzung um weitere Features beginnen. Wie wäre es mit hierachischen Zustandsmaschinen? Timer sind etwas sehr nützliches. Und wie wäre es mit nebenläufigen Zustandsmaschinen? Sehr leicht ist es, die Zustände um Aktionen zu bereichen. Ein Feature aus der UML sind Aktionen beim Eintritt (entry) und beim Verlassen (exit) eines Zustands. Transitionen kann man um Guards erweitern. Guards knüpfen das Ereignis an eine Bedingung, die zusätzlich erfüllt sein muss.

Sie werden feststellen, dass es gar nicht so schwer ist, sich ein XML-Schema für sehr komplexe und mit allen Featuren ausgerüstete Zustandsmaschinen auszudenken und dazu einen Code-Generator zu entwickeln.

(P.S.: Die an dieser Stelle zuvor berichteten Probleme mit dem Syntax-Highlighting haben sich gelöst. Allerdings scheint der Chrome-Browser Schwierigkeiten bei der Darstellung des letzten Code-Auszugs, dem Python-Code, zu haben. Strange! Kennt jemand einen Workaround?)

Freitag, Januar 16, 2009

Nominiert für die Auslese08

Ich rechne es meiner treuen Leserschaft zu, zwei meiner Beiträge für die Auslese08 im Wissenschaftscafe nominiert zu haben. Vielen Dank dafür -- es freut mich natürlich sehr, zur Vorschlagsliste der 80 Blogbeiträge zu gehören. Wenngleich ich mir keine Hoffnungen mache, in die engere Auswahl zu kommen. Ich habe auf noch keiner Party die Stimmung hochgepeitscht mit einer Frage wie "Was ist ein System?". Und Gedanken zu "Syntax und Semantik" lassen auch nicht jedes Herz höher schlagen :-) Spaß macht es mir dennoch, und es ist schön zu wissen, dass zumindest einige Leser solche Freuden mit mir teilen.

Freitag, Januar 09, 2009

Let it float!



Minus 50 plus 50 macht Null -- in manchen Fällen muss man sich so ein Ergebnis autorisieren lassen. Plus 38.29 minus 38.29 macht auch Null und -- wenn man es nicht so genau nehmen will -- sogar reich!

Von diesem schönen und oben abgebildeten Fehler berichtet Michael Haupt auf seinem Blog in dem Beitrag "Ich bin reich!". Ein Fehler mit Pilgerwert. Ob ich irgendwann einmal diesen Zettel anfassen und in den Händen halten darf? Das sollte ich bei meinem nächsten Besuch in Potsdam tun!

Herr Haupt wird genauso wie ich innigst hoffen, dass der Fehler von keinem unserer Studierenden "eingebaut" worden ist. Denn der Fehler ist fast so alt wie der erste Computer. Fließkommszahlen, floats, sollte man niemals auf Gleichheit überprüfen. Rundungsfehler können einem einen gewaltigen Strich durch die Rechnung machen (siehe oben). Finanzrechnungen sollte man stets genau rechnen, zum Beispiel in Cents und mit Integern und einem definierten Verhalten bei Rechnungen, die nicht in Cents sondern in gebrochenen Cents (z.B. bei einer Division) aufgehen.

Sonntag, Dezember 14, 2008

Denken unter 150

184 Puls. Die Zone, in der ich nur noch renne. Kein Gedanke, kein denken -- nur kämpfen. Ich laufe gegen meinen Körper an. Längst bin ich im anaeroben Bereich. Ich kann nicht mehr soviel Sauerstoff in meine Lungen pumpen, wie ihn mein Körper benötigt. Ich spüre regelrecht, wie meine Zellen auf Hochtouren laufend die letzten Energien mobilisieren. Alles schmerzt. Warum tue ich mir diesen Berg eigentlich an? Gleich ist Schluss. Noch 23 Sekunden, die muss ich noch durchhalten, irgendwie.

"Wie lautet die Binärzahl von 197?"

Wie bitte? Die Aufgabe hämmert in meinem Kopf. Noch 18 Sekunden. Ich kann jetzt nicht rechnen. Ich schaue auf die Uhr. Noch 16 Sekunden den Körper durch die Qual treiben. Mein Puls fällt ab. Ich kann das Tempo nicht mehr halten. 197 ist kleiner als 256 und größer als 128. Mehr bekomme ich reflexartig nicht hin. Kämpfen steht im Vordergrund. Bei dem Puls kann ich keinen klaren Gedanken fassen. Wie auch? Ich versuche gerade meinen Körper zu bezwingen, das Limit noch für einen Moment aufrecht zu erhalten. Noch 6 Sekunden. Wie langsam die Zeit vergehen kann. Nicht aufgeben. Noch 4 Sekunden. Ja, gleich -- in meinem Kopf tobt der Wahnsinn. Ich kämpfe mit jedem Gedanken gegen all die Körpersignale an, die nicht mehr in 2 Sekunden, sondern sofort aufhören wollen. Da, 0 Sekunden. Erlösung.

Stoppuhr an!

Wie lange dauert es, bis ich einen "Denkpuls" erreicht habe? Bis sich mein Körper soweit von der Belastung erholt hat, dass mein Hirn wieder bereit für Input ist? Dass es bereit ist für eine Aufgabe? Dass es die Aufgabe lösen kann?

Im Moment geht gar nichts. Ich atme tief und heftig. Spucke auf den Asphalt. Luft. Luft. Ich will nur atmen. In meinen Ohren, überall hämmert der Puls. Aber er fällt. Jetzt ist er unter 176. Denken? Nein. Geht nicht. Ich gehe. Die Schmerzen lassen nach. Der ganze Körper lechzt nach Erholung.

Ab welchem Puls kann ich die Aufgabe lösen? Wie lautet die Binärzahl von 197?

Erste Denkreflexe melden sich. 197 ist größer als 128, hatten wir schon, also haben wir "ganz links" eine 1. Ungerade Zahl, also "ganz rechts" eine 1. Sonst? Achja, 8 Bits reichen.

Mehr geht nicht. Mein Puls fällt. So langsam regelt sich die Atmung wieder ein. 160 Puls. Erste Gedanken raten mir zu einem systematischen Vorgehen. Nächste Bitstelle? 64. Was ergibt 128 plus 64. Es fühlt sich nach weniger als 197 an. Wieviel weniger? Eine einfache Addition. Im Kopf -- derzeit unmöglich. Ich brauche noch etwas Pause.

Ich wage einen neuen Versuch. Puls knapp über 150. 128 plus 64 macht etwas mehr als 180, fehlen noch 8 plus 4, also 12. 192, das könnte sein. Ich habe noch kein Vertrauen in meine Denke. Vieles ist mechanisch abgeleitet. Eine echte Kontrolle im Hirn fehlt. Dafür bin ich noch zu sehr aus der Puste.

Ich spekuliere weiter. 128 plus 64 macht 192. Die 197 war gegeben, bleiben 5 übrig. Eine 1 hatte ich schon für das Ungerade-sein spendiert. Bleiben noch 4 übrig. Ok. Bitmuster "ganz rechts" 101, Bitmuster "ganz link" 11. Ich komme etwas zur Ruhe. Bin kurz vor 140 Puls. Jetzt weiß ich's. Ich habe fünf Bitstellen berechnet, noch drei weitere Bitstellen mit Null fehlen. Ergebnis: 11000101. Uff.

Hätte ich mir eine ernstere Aufgabe gestellt, ich bin mir sicher, erst in einem Bereich von 120 bis 130 Puls wäre eine Problemlösung möglich, mit ersten sinnvollen Gedanken um die 140 abwärts.

Angeregt zu diesem (virtuellen) Experiment hat mich das Buch von Josh Waitzkin: The Art of Learning -- An Inner Journey to Optimal Performance, Free Press, 2007. Er beschreibt in diesem Buch seine Lernerfahrungen als junges Schachgenie und später als Kampfsportler. In beiden Disziplinen ist bzw. war er sehr erfolgreich. Wem weder das Schachspiel noch der Kampfsport fremd sind, der wir seine Freude an diesem Buch haben und es vielleicht genauso wie ich verschlingen. Denn faszinierend sind seine Berichte und Einsichten schon, praktisch und anwendbar sind sie auch. So berichtet er unter anderem davon, wie wichtig der Zusammenhang zwischen körperlicher Fitness und der Fähigkeit zu sehr kurzer geistiger Erholung ist -- um Hochleistungen erbringen zu können, geistige ebenso wie sportliche. Es ist auch ein Weg, um bessere Klausuren schreiben und Prüfungen ablegen zu können.

Das ist die eine Buchempfehlung, die ich zu Weihnachten für Sie habe. Die andere ist ein Buch von John Medina: Brain Rules -- 12 Principles for Surviving and Thriving at Work, Home, and School, Pear Press, 2008. Und da erfahren wir, dass schon ein wenig körperliche Ertüchtigung die geistige Performanz boostet. Das ist Regel Nr. 1. Weitere elf Regeln verraten Ihnen noch mehr darüber, wie unser Gehirn tickert und wie wir gut mit ihm umgehen können. Selbst wenn Sie sich für das Buch nicht weiter interessieren, besuchen Sie die Webseite zu den Brain Rules -- es lohnt sich.

Montag, November 17, 2008

Syntax und Semantik

Was ist Syntax, was ist Semantik? Diese zwei Begriffe beschäftigen mich immer wieder, siehe zum Beispiel auch "Uniform Syntax" (23. Feb. 2007). Beide Begriffe spielen eine entscheidende Rolle bei jeder Art von maschinell-verarbeitbarer Sprache. Vom Dritten im Bunde, der Pragmatik, will ich an dieser Stelle ganz absehen.

Die Syntax bezieht sich auf die Form und die Struktur von Zeichen in einer Sprache, ohne auf die Bedeutung der verwendeten Zeichen in den Formen und Strukturen einzugehen. Syntaktisch korrekte Ausdrücke werden auch als "wohlgeformt" (well-formed) bezeichnet.

Die Semantik befasst sich mit der Bedeutung syntaktisch korrekter Zeichenfolgen einer Sprache. Im Zusammenhang mit Programmiersprachen bedeutet Semantik die Beschreibung des Verhaltens, das mit einer Interpretation (Auslegung) eines syntaktisch korrekten Ausdrucks verbunden ist.

[Die obigen Begriffserläuterungen sind angelehnt an das Buch von Kenneth Slonneger und Barry L. Kurtz: Formal Syntax and Semantics of Programming Languages -- A Laboratory Based Approach, Addison-Wesley, 1995, Kapitel 1.]

Die Grenze zwischen Syntax und Semantik ist fließend und lässt sich schön am Beispiel mit Hilfe regulärer Ausdrücke erläutern. Spezifizieren wir eine Syntax für die Angabe der Uhrzeit. Gemeint sind minutengenaue Zeitangaben im 24-Stunden-Format.

Ein einfacher regulärer Ausdruck beschreibt die Syntax sehr direkt: "\d\d:\d\d" -- dieser Ausdruck kam schon in der Einführung "Reguläre Ausdrücke" zur Anwendung und ist dort ausführlich beschrieben. Dieser reguläre Ausdruck ist zu allgemein, denn die Angabe "14:72" wäre genauso möglich wie "25:13". Das sind keine Uhrzeitangaben. Ein regulärer Ausdruck, der nur gültige, 4-stellige Zeitangaben erlaubt, ist "([01]\d)|(2[0-3]):[0-5]\d".

Worauf es mir hier ankommt, ist Folgendes: Der erste reguläre Ausdruck spezifiziert eine Uhrzeitsyntax, die so allgemein gefasst ist, dass eine Nachverarbeitung (Teil der Semantik(!)) prüfen muss, ob die Kombination von Stunden und Minutenangaben eine gültige Uhrzeit ist. Der zweite reguläre Ausdruck hingegen erfasst gültige, vierstellige Uhrzeitangaben vollständig syntaktisch. Jegliche Weiterverarbeitung, sprich jegliche Semantik kann sich darauf verlassen, mit ausschließlich gültigen Uhrzeitangaben zu arbeiten.

Wir beobachten an diesem Beispiel, dass es einen Spielraum gibt, was Syntax und was Semantik in einer Sprache ist. Je allgemeiner die Syntax, desto mehr Form- und Strukturwissen muss als Teil der Semantik behandelt werden. Je spezifischer die Syntax ist, desto weniger muss sich die Semantik um eine Erkennung bemühen. Im Idealfall ist die Semantik von aller Klärung befreit, wenn die Syntax eindeutig ist.

Im Fall der Uhrzeit ist es relativ leicht möglich, Uhrzeitangaben vollständig syntaktisch zu spezifizieren. Das geht nicht immer. Nehmen wir Datumsangaben. Eine vollständige syntaktische Spezifikation, welche Monate 30 bzw. 31 Tage haben, ist noch machbar -- ob sie sinnvoll ist, ist die Frage. Denn spätestens für den Monat Februar ist mit regulären Ausdrücken nicht mehr entscheidbar, ob 28 oder 29 Tage die korrekte Angabe sind. Es sind 29 Tage, sofern das Jahr ein Schaltjahr, also durch 4 teilbar ist. Es bleibt jedoch bei 28 Tagen, wenn die Jahreszahl durch 100 teilbar ist, nicht jedoch durch 400. Eine Datumsangabe kann nicht vollständig syntaktisch erkannt werden -- es sei denn, die Syntax hätte Rechenfähigkeiten.

Und damit sind wir genau an dem Punkt angelangt, was Syntax von Semantik unterscheidet: Syntax ist der Anteil einer Sprachdefinition, der gültige Zeichenfolgen einer Sprache erkennt (Form- und Strukturerkennung) und zwar mit einem Formalismus, der keine Rechenfähigkeit hat, sprich, der nicht Turing-äquivalent ist. Dieser syntaktische Anteil liefert die Vorverarbeitung. (In der Theorie der formalen Sprachen sind damit reguläre (Typ-3) bzw. kontextfreie (Typ-2) Grammatiken gemeint, siehe Chomsky-Hierarchie.). Die Semantik betrifft alle weitere Symbol-Verarbeitung, die mit einem Formalismus beschrieben ist, der Turing-äquivalent ist.

Mit diesem Verständnis von Syntax und Semantik kann man sinnvoll den Begriff der syntaktischen Obergrenze definieren: Die syntaktische Obergrenze reizt die Form- und Strukturerkennung aus und minimiert den semantischen Anteil, der eventuell notwendig ist, um die Erkennung von Formen und Strukturen eindeutig zu machen. Zum Beispiel beschreibt ein regulärer Ausdruck die syntaktische Obergrenze für eine Datumsangabe im Format "Tag-Monat-Jahr" (TT-MM-JJJJ), wenn semantisch einzig die Tagesangabe für den Februar überprüft werden muss. Das Muster "\d\d-\d\d-\d\d\d\d" liegt offensichtlich nicht an der syntaktischen Obergrenze. Der Semantik-Anteil muss umfangreiche Gültigkeitsprüfungen vornehmen.

Man kann in diesem konkreten Beispiel auch auf einen Formalismus für die Syntax zurückgreifen, der zwar Rechenfähigkeiten hat, in seinem Verarbeitungshorizont jedoch strikt begrenzt ist. Dann liegt eine kontextsensitive Grammatik vor (Typ-1). Kontextsensitive Grammatiken sind eher die Ausnahme. (Anmerkung: Grundsätzlich ist auch ein Formalismus für die Syntax denkbar, der Turing-äquivalent ist (Typ-0), allerdings fällt dann die Grenzziehung zwischen Syntax und Semantik schwer. Ein anderes Kriterium muss dann gefunden werden.)

Meine These ist, dass diese Zerlegung einer Verarbeitung in einen Anteil, der aus einem nicht Turing-äquivalenten Mechanismus und einem Turing-äquivalenten Mechanismus besteht, ein stets wiederkehrendes Thema in der Informatik ist. Pipe-Filter-Architekturen sind ein Beispiel, die Zerlegung eines Programms in Interfaces und Verhalten ein anderes. Interessanterweise habe ich bislang keinen Hinweis darauf gefunden, dass dieses Zerlegungsmuster bereits von jemand anderem postuliert worden wäre. Ich glaube, man könnte dieses Zerlegungsmuster sehr viel systematischer in der Software-Entwicklung einsetzen.

Ein Anmerkung, siehe im vorletzten Absatz, ist hinzu gekommen, um die Diskussion etwas abzurunden. (2008-11-18)

Dienstag, November 04, 2008

Studieren mit der PS3


Ungefähr 1000 PS3-Spielkonsolen dürften in etwa der Rechenleistung unseres Gehirns gleichkommen. Vielleicht ist das eine gewagte Schätzung, so ganz seriös sind solche Computer/Hirn-Vergleiche nie. Aber wer als "Niko" Bellic in die Welt von GTA IV eintaucht, dem wird diese Zahl vermutlich gar nicht so abwegig vorkommen. Es ist unglaublich, wieviel Welt so eine Spielkonsole simulieren kann.

In der PS3 leistet ein Spezialprozessor, der Cell-Chip, diese beachtliche Arbeit. Ein etwa 10 Jahre altes Rechenzentrum im Schachtelformat. Mehrere Einheiten arbeiten parallel und fressen Zahlen, was das Zeug hält.

Es hat eine ganz eigene Faszination, wenn man Linux auf die PS3 installiert und sich die Konsole zum ersten Mal meldet. Man hat Kontakt mit dieser geballten Rechenkraft aufgenommen. Plötzlich gehorcht sie einem aufs Wort. Und irgendwie hört man die Maschine nach Arbeit rufen. Das bisschen Linux, die paar Befehle, die über die Konsole reinwandern, die machen so ein Maschinchen nicht müde. Da steht ein Apparat, der es gewohnt ist, Sie durch Häuserschluchten zu jagen, Ihnen Aliens aufzuhetzen, Sie unter Beschuss zu nehmen und Ihnen wilde Verfolgungsjagden zu liefern. Diese Maschine hat Hunger! Diese Maschine sucht die Herausforderung und die Auseinandersetzung!

Nehmen Sie die Herausforderung an?

Geben Sie der Maschine Futter, programmieren Sie die PS3 selbst! Jagen Sie die PS3 mit Ihren Programmen an den Rand des Machbaren! Fangen Sie da an, wo der PC schlapp macht!

Einen Start bietet Ihnen unsere Webseite http://ps3.hs-heilbronn.de.