Einführung

In diesem Kapitel lernen wir ein paar Leute kennen, die im nächsten Kapitel eine Rolle spielen. Wer nicht wissen will woher sie kommen, der kann gleich beim nächsten Kapitel weiter-lesen.

An unserem Kleinstadt-Gymnasium wurde von Schülern der höheren Klassen erwartet, dass sie den Jüngeren Nachhilfestunden gaben für ein eher symbolisches Entgelt (ich glaube es waren 50 Pfennige, somit ein 1/4-EUR job). So wurde ich eines Tages ins Direktorat beordert und mit der mathematischen Förderung von Peter beauftragt. Peter war ein phantasievoller Junge, der die Nachhilfestunde am liebsten mit der Diskussion seiner gerade entstehenden und verblüffend illustrierten Science Fiction Story verbracht hätte. Wir kamen aber doch noch zu den Übungsaufgaben im Buch und begannen mit einer Aufgabe, die Ihnen vermutlich schon einmal begegnet ist: Das Buch bot eine antiquierte Einkleidung des Problems und fragte ob unter einem Seil, das man einmal eng anliegend um die Erdkugel gelegt hat, dann durch Einknoten eines Zusatzstricks um einen Meter verlängert und von einem Heer hilfreicher Eng'lein hat anheben lassen bis es wieder straff ist - ob also nach dieser Prozedur eine Ameise, eine Maus, oder ein Hase unter dem Seil hindurch kriechen könnte. Peter fand die Frage amüsant und dachte sich ganz in die Situation hinein. Mit der himmlischen Art des Seil zu straffen hatte er kein Problem, aber ob sich das Seil nicht dehnen würde angesichts seiner enormen Länge gab er zu bedenken. Meine Erklärung man müsse sich das Seil in dieser Aufgabe 'natürlich' als un-dehnbar vorstellen nahm er sichtlich missmutig auf. Damit sei es aber kein Seil mehr und dann würden wir wieder bloß von 'so geometrischem Zeug' reden, was es ja nicht wirklich gäbe. Trotzdem machte er sich auf den üblichen Lösungsweg, kramte die richtige Formel für den Kreisumfang aus dem Gedächtnis und hatte bald das Ergebnis: ca. 16 cm schwebt das Seil über dem Boden. Wir waren uns einig, dass zumindest Peters Kaninchen keine Schwierigkeit hätte, darunter hindurch zu kriechen. Ich sagte Peter etwas Anerkennendes über sein Rechengeschick und er konterte mit der Frage, ob ich denn das Ergebnis der Rechnung glaube - er jedenfalls könne eine so völlig unplausible Behauptung nicht akzeptieren. Natürlich hielt ich Peter vor, er hätte das Ergebnis doch selbst ausgerechnet. Aber das war, ich ahnte es schon, kein gutes Argument.

Peter stellte richtig, er habe nur das nachgemacht, was man ihm für ähnliche Situationen vorgekaut hätte. Für ihn hätten diese Überlegungen - wenn sie diesen Namen denn verdienten - nicht die geringste Überzeugungskraft. Es sei doch absurd den Durchmesser der Erde einfach als einen Buchstaben zu behandeln, mit diesem dann irgendwie auf dem Papier herumzurechnen und zu glauben das Ergebnis hätte dann etwas mit der Erde, oder einem Seil, zu tun. Jedenfalls hätte er nie etwas gehört, was ihm einen solchen Zusammenhang plausibel gemacht hätte - alle Erklärungen von Lehrern und in Büchern würden doch diesen formalistischen Unsinn schon voraussetzten, anstatt ihn zu erklären. Solchen Unsinn für bare Münze nehmend, könnte man natürlich jede Menge weiteren Unsinn entwickeln, der anfängliche Unsinn würde deswegen nicht überzeugender.

Als Freund von Spionagegeschichten verstand Peter meinen Hinweis , dass eine erdachte Geschichte - wie die fingierte Vergangenheit eines Spions - viel schwieriger überzeugend weitergesponnen werden kann, als sich eine wahre Geschichte von selbst entwickelt. Nun - von Wahrheit werde ich ja wohl nicht sprechen wollen bei den gekünstelten Begriffen der Mathematik, meinte Peter, aber etwas sei vielleicht dran an dem Vergleich. Der unterstellte Umstand, dass sich die Mathematiker nicht immer in Widersprüche mit ihren eigenen Geschichten verwickeln schien ihm zu einer Art Rechtfertigung zu taugen. Allerdings - Peter begann schon wieder zu zweifeln ob die Mathematik wenigstens als gekonntes Lügengespinst Anerkennung verdiene - was wäre leichter als solche Widersprüche bewusst zu übersehen oder diskret unter den Teppich zu kehren? Ich führte ins Feld, dass schon mancher Mathematiker dadurch berühmt geworden sei, dass er einen Fehler im Gedankengebäude eines berühmten Kollegen aufgedeckt hätte, so dass es also einen starken Anreiz gebe, den Unrat unter den Teppichen hervorzuholen. Bei fachlichen Einzelheiten mag das ja so sein, räumte Peter ein, aber wenn, wie er glaube, die ganze Mathematik auf einer Fiktion beruhe - Mathe Fiction sozusagen - dann wären die Mathematiker wohl die Letzten, die ein Interesse hätten, diesen Tatbestand bekannt zu machen.

Die Diskussion hatte damit einen Grad von Allgemeinheit erreicht auf dem man auch die unsinnigste Behauptung plausibel erscheinen lassen kann, wenn es einem an Argumentationskunst nicht mangelt. Ich versuchte daher daher Peters sprudelnden Gedankenfluss wieder auf Konkreteres zu lenken. Auf dem Tisch stand eine große runde Keksdose aus Blech und ich musste nur sagen, diese sei ja ebenso rund wie die Erde, da war Peter schon aufgesprungen und kam mit dem Schneidermaßband seiner Mutter zurück und schlug vor die Sache mit der Seilverlängerung im kleinen Maßstab auszuprobieren. Im Reden überfiel ihn die Einsicht, auf die die Aufgabe hinlenken soll: zwar hatte ich während Peters Rechnung darauf hingewiesen, dass sich also der Radius der Erde 'herausgehoben' hätte. Aber Peter hatte das nur insoweit aufgenommen, dass es also nicht nötig sei, den angegebenen Wert für den Erdradius tatsächlich in eine Formel einzusetzen - Glück gehabt!. Jetzt war ihm aufgegangen, dass die Folge davon ja sein müsse, dass genau dieselben sechzehn Kaninchen-tauglichen Zentimeter auch bei unserem Keksdosenversuch herauskommen würden, wenn es tatsächlich nach der gemachten Rechnung ginge.

Ich bremste Peters Versuchsvorbereitungen und tat so, als ob ich auch nicht sicher wäre, wie der Versuch ausgehen würde. Solange wir das Ergebnis noch nicht wüssten, drängte ich Peter, sollten wir uns klar werden, welche Lehre wir aus dem Ausgang zu ziehen hätten. (Ein lebenskluger Freund meiner Eltern pflegte scherzhaft zu sagen, die sicherste Art sein Leben lang dumm zu bleiben sei, nach jeder Entscheidung zu vergessen, was man vor der Entscheidung gedacht hat.) Peter sagte: "Natürlich wäre ein Ergebnis von sechzehn Zentimetern ein starkes Argument für ..." dabei machte er Kreis-zeichnende Handbewegungen und plötzlich wich die Spannung von seinem Gesicht und machte einem melancholischen Lächeln Platz: die Handbewegungen hatten ihm gezeigt, dass das Ergebnis nicht weit weg vom Rechenergebnis liegen würde. So war es dann auch - bei unserer Keksdose, und auch bei einer Apfelsine. Den Fall eines Rundkörpers von verschwindend kleinem Durchmesser spielten wir dann nur noch im Kopf durch: Ein Kreis, dessen Umfang 1 Meter lang ist, hat einen Radius von etwa 0,016 Metern. Der genaue Zahlenwert ist natürlich - ausgedrückt durch die berühmten Kreiszahl π = 3.14159... - nichts anderes als 1/(2π).

Ich fragte Peter, wo sich in diesem Fall nach seiner Sicht der Dinge die mathematische Fiktion versteckt hätte. Eigentlich, meinte er, sei hier wohl alles mit rechten Dingen zugegangen: alles was wir hier gebraucht hätten sei ja auch in der Schule irgendwie erklärt worden - wie Längen nach Wahl einer Längeneinheit durch Zahlen beschrieben werden, wie mit den Zahlen gerechnet wird, wie Eigenschaften geometrischer Figuren durch Gleichungen zwischen Zahlen ausgedrückt werden; ja sogar in der Physik scheine das zu funktionieren, wie in der ihm wohl bekannten Formel E = m c2 ... . Ich fand Peters Zustimmung zur Feststellung, dass im gegebenen Fall die 'mathematische Methode' jedenfalls funktioniert hätte, die ich nochmals zusammenfasste in der Form: Eigenschaften realer Objekte kann man in einer Weise durch Zahlen und Beziehungen zwischen Zahlen beschreiben, dass mathematische Manipulationen mit diesen Zahlen und Beziehungen zu Ergebnissen führen, die Aufschluss über Eigenschaften realer Objekte geben.

Lesefrüchte aus Paul Karlsons prächtigem Buch "Vom Zauber der Zahlen" wiedergebend, erzählte ich Peter wie diese Methode sich zaghaft bei den alten Babyloniern entwickelte und von Pythagoras und seinen Schülern zu großem Erfolg in mannigfachen Anwendungsfeldern geführt wurde. Alte Geschichte interessierte Peter sehr; er war weniger geneigt den alten Griechen einen Hang zum Unfug zuzutrauen, als seinen Zeitgenossen. Jedenfalls verbesserte sich Peters Verhältnis zur Mathematik beträchtlich und seine Schulnoten folgten dieser Entwicklung in erfreulichem Maße.

Bald nach dieser Diskussion mit Peter fragte ich meinen eigenen Mathematiklehrer (dessen Unterricht den meisten Schülern Vergnügen bereitete und der allen Versuchen an seiner Autorität --- der eines kleinen, zart gebauten, dünnhäutigen Mannes --- zu kratzen mit vernichtendem Humor zu begegnen wusste) ob er sich vorstellen könne, dass es in der Mathematik innere Widersprüche gebe, die nur noch niemandem aufgefallen seien. Zunächst einmal, antwortete er, muss ein Mathematiker sich alles vorstellen können. Aber, um auf den Kern deiner Frage zu kommen: Es haben sich tatsächlich Generationen von Mathematikern damit geplagt einen Beweis für die Widerspruchsfreiheit der Mathematik zu finden. Ganz selbstverständlich ist es ja nicht, was das überhaupt bedeuten soll: 'frei von Widersprüchen', wo man doch von den Widersprüchen, die man in Betracht zu ziehen hat, keine Ahnung hat. Jedenfalls hat man die Sache soweit eingegrenzt, dass man in einem präzisen Sinn sagen kann: die Mathematik ist widerspruchsfrei, oder sie ist es nicht. Natürlich glaubten die Mathematiker dass die erste dieser zwei Möglichkeit zutrifft und manche mühten sich einen mathematischen Beweis dafür zu finden. Vergebliche Mühe, stellte sich im Jahr 1931 heraus: man hatte streng bewiesen, dass es einen solchen Beweis nur dann geben kann, wenn die zweite Möglichkeit vorliegt, wenn die Mathematik also Widersprüche birgt. In diesem Fall ist der Beweis natürlich nichts wert, weil er dann ja anerkanntermaßen auf falschen Annahmen beruht. Der Entdecker dieses unerwarteten Sachverhalts hieß Kurt Gödel und war damals gerade 25 Jahre alt. Zwar schon Doktor, aber erst dabei sich irgendwo in seiner Heimat Österreich eine Stelle als Privatdozent zu ergattern. Heute ist die Sache mit der Unbeweisbarkeit der Widerspruchsfreiheit der Mathematik als 'Zweiter Gödelscher Unvollständigkeitssatz' bekannt.

Das war ja eine schöne Bescherung! Stand es damit um die Glaubwürdigkeit der Mathematik nicht noch schlechter als von Peter geargwöhnt? Und wenn man sich auf die Mathematik nicht verlassen kann, worauf dann? Plötzlich bekam die Sache eine andere Dimension: Wäre die Sache anders gelaufen und hätte man die Widerspruchsfreiheit der Mathematik beweisen können, was wäre damit gewonnen gewesen? Hätte dieser Beweis für jedermann, also etwa für Peter, Überzeugungskraft? Worauf würde diese beruhen? Fragen über Fragen, auf die mir das, was ich von der Mathematik bisher kennen gelernt hatte, keine Antwort bot.

Die Legende um das Nachspiel zur Erfindung des Schachspiels

Die Geschichte ist oft erzählt worden: Der indische Weise Sessa erfand das Schachspiel um seinem König Sheram ein gleichnishaftes Abbild für das Funktionieren seines Reiches zu geben: In Zeiten von Not und Krieg kann der König selbst wenig bewirken, wenn er nicht Schutz und Hilfe durch seine Adeligen und seine Bauern bekommt. Auch ein Bauer kann im Laufe der Geschehnisse zum Retter des Reiches werden. Sheram nahm das Spiel begeistert auf und bot Sessa eine Belohnung nach dessen eigener Wahl. Sessa erbat sich ein Weizenkorn für das erste Feld des Schachbretts, zwei Körner für das zweite Feld, vier Körner für das dritte Feld, also immer für das folgende Feld doppelt so viele Körner, wie für das eben betrachtete. Das Schachbrett hat 64 Felder, wahrscheinlich weniger als der König Köche beschäftigte, und so war sich Sheram sicher, dass mehr als ein paar Säcke Weizen als Sessas Lohn nicht herauskommen würden. Darüber war Sheram verärgert - er sah sich als königlicher Wohltäter nicht recht ernst genommen. Barsch befahl er seinem Schatzmeister sich um die rasche Übergabe des unwürdigen Geschenks zu kümmern. Die Vollzugsmeldung blieb länger aus, als er zu warten gewohnt war und so lies er den Schatzmeister herbeibringen. Den fand er im Zustand höchster Verzweiflung: er könne sich der Sache noch nicht völlig sicher sein, aber es deute sich an, dass Sessas Wunsch nicht erfüllt werden könne, auch wenn man alle Getreidespeicher des Reiches leere.

Damit wollen wir die Geschichte verlassen und uns selbst die Frage stellen, wie viele Weizenkörner der Schatzmeister aufzutreiben hätte. Nicht gänzlich ahnungslos, wie wir sind, liegt es uns nahe den Rechenausdruck

1 + 2 + 2⋅2 + 2⋅2⋅2 + ...

zum Ausgangspunkt der Lösung zu machen. Allerdings, so überlegen wir, wird das eine verdammt lange Rechnerei, wenn man sich aufs handschriftliche Rechnen nach den Schulregeln einlassen würde - und für einen Taschenrechner würden die Zahlen bald zu lang. Stellen wir uns vor, wir hätten die Rechnung tatsächlich ausgeführt, uns auch nicht verrechnet, und also (was auch anders?) 18446744073709551615 als Ergebnis erhalten. Dann hätten wir unsere Erkundungstour durch Matheland damit begonnen, dass wir die Grundrechenarten für ganze Zahlen, und damit erst recht diese Zahlen selbst, schon als etwas Bekanntes akzeptiert hätten. Wir könnten uns darauf berufen, dass wir das ja alle schon in der Grundschule (bei mir hieß es noch Volksschule) gelernt haben. Für das vor uns liegende Unternehmen wäre das aber eine vergebene Chance. Sollten wir nicht gerade die einfachsten mathematischen Objekte - und das sind die Zahlen nun mal - als Prüfstein für unsere geistige Klettertechnik nützen, anstatt gleich die erste Felsstufe mit einer Leiter zu überwinden?

Jeder schulmäßige Zugang mogelt da ganz gewaltig. Er baut - für die Schule völlig zu Recht - auf die Überzeugungskraft des verallgemeinerungsfähigen Beispiels. Durch Übertragung des Beispiels auf ähnlich gelagerte Fälle entsteht (im Idealfall) eine Sicherheit der Anwendung, die auf eine genaue Formulierung allgemeiner Gesetzmäßigkeiten verzichten kann. Jedenfalls bleiben die Mittel im Dunklen, die nötig sind solche allgemeinen Gesetzmäßigkeiten zu beweisen. Mit köstlicher Ironie wird diese Situation in Edmund Landaus berühmtem Büchlein "Grundlagen der Analysis" (1930) angesprochen, das laut Verfasser "zum Teil in usum delphinarum [als Lektüre für Mädchen] geschrieben ist, indem meine Töchter bekanntlich (siehe E. Landau, Vorlesungen über Zahlentheorie, Bd. 1, S. V) schon mehrere Semester studieren (Chemie), schon auf der Schule Differential- und Integralrechnung gelernt zu haben glauben und heute noch nicht wissen warum

x⋅y = y⋅x

ist". Ich möchte einen Weg vorschlagen auf dem wir Landaus Töchter in dieser Hinsicht weit hinter uns lassen, allerdings einen anderen als Landau ihn beschrieben hat.

Dazu können wir all unsere Vorkenntnisse des Zahlen-Rechnens (Arithmetik) vergessen, denn wir werden uns die Zahlen als Mittel zur Lösung des Sessa-Problems selbst erschaffen. Es wird sich herausstellen, dass die wichtigsten mathematischen Fragestellungen, die uns auf unserem Weg begegnen werden, keimhaft in dieser Aufgabe "das Zahlenreich zu erschaffen" enthalten sind. Wovon können wir angesichts dieser Aufgabe ausgehen? Oder - wie Paul Taylor in seinen 'Practical Foundations of Mathematics' fragt: "How do we begin to lay the foundations of a palace which is already more than 3600 years old?" Bei solch bedeutsamen Fragen ist Inspiration gefragt und in unserem Fall ist eine Münchner Bierwirtschaft der richtige Ort derselben teilhaftig zu werden. Ob wir nun selbst hinter einem Spezi sitzen, oder am allgemeinen Leeren der Bierkrüge teilnehmen - uns wird nicht entgehen, wie die Kellnerin jeden neu herbeigebrachten Bierkrug auf dem zugehörigen Bierdeckel als Strichlein notiert. Muss der Gast dann weiterziehen, hat die Kellnerin auch ohne herausragendes Gedächtnis keine Schwierigkeit den richtigen Preis zu verlangen.

Es leuchtet in diesem Fall ein, dass die Strichliste auf dem Rand des Bierdeckels und die "Anzahl der gebrachten Bierkrüge" in einem so engen Zusammenhang stehen, dass es nicht nötig ist zwischen diesen beiden Kenngrößen für denselben Vorgang zu unterscheiden (jedenfalls, wenn es um's Bezahlen geht). Dies gibt unseren ersten Weg ins Reich der Zahlen vor: Zahlen als Strichlisten. Für diesen Weg möchte ich vorschlagen: Gehen wir zügig und heben uns unsere Beobachtungen und eventuellen Irritationen bis zum Ende des Wegs auf, den wir dann rasch erreicht haben werden. Dann haben wir eine gute Basis darüber zu sprechen.

Unter einer Strichliste wollen wir eine endliche Zeichenkette verstehen, die durch Wiederholung eines einzigen Zeichens entsteht, nämlich des Schrägstrichs '/'. Ist damit der Begriff der Strichliste so exakt festgelegt (definiert), wie es die Mathematik für ihre Begriffe fordert? Die Antwort ist eindeutig "ja". Es ist eine allgemeine Haltung der Mathematik, dass 'der mechanische Umgang mit Zeichenketten', der Teil ihrer Aktivitäten ist, der nicht mehr hinterfragt zu werden braucht. Meist erfolgt dazu der Hinweis, dass sich dieser Umgang (weil 'mechanisch') ja durch ein Computerprogramm nachbilden lässt und so auch außerhalb der Gedankenwelt von Mathematikern nachprüfbar ablaufen kann.

Machen wir uns klar, wie der 'mechanische Umgang mit Zeichenketten', also die mathematische Arbeit auf niedrigstem Niveau, konkret für Strichlisten aussehen soll: Die folgenden Seiten dieses Buches können dabei als Beispiel herhalten, das alle wichtigen Gesichtspunkte verdeutlicht: Strichlisten werden eingebettet in erläuterndem Text hinzuschreiben sein. Die Gliederung der Schreibfläche und die verwendeten Zeichen haben die übersichtliche Beschaffenheit eines mit Computer und Schreibprogramm erstellten Textes. (Natürlich kann auch ein handschriftlicher Text diese Eigenschaft haben, mir fällt es aber nicht leicht den Beweis dafür anzutreten.) Trotz dieser Übersichtlichkeit ist es nützlich für die Niederschrift von Strichlisten eine Kennzeichnung/Markierung zu vereinbaren, um sicher zu sein, dass etwa der eben künstlich verwendete Schrägstrich zwischen zwei Substantiven keine Strichliste im Rahmen unserer "Strichlisten-Mathematik" sein soll. Als solche Kennzeichnung ist der Einschluss zwischen halbe Anführungszeichen praktisch, also die Schreibweise '////' für die Strichliste, die man ohne diese Vereinbarung als //// schreiben würde. Mit der Klärung der Kennzeichnungsfrage ist uns eine Möglichkeit entstanden, die sich als äußerst nützlich erweisen wird: Neben den Strichlisten '///', '//', '/' fällt es nicht schwer auch '' als "Strichliste ohne Strich" oder als "leere Strichliste" anzuerkennen. Wir verabreden ausdrücklich, den Begriff der Strichliste so zu verstehen, dass dieser Fall eingeschlossen ist. Die Mathematik fragt von den Objekten ihres Interesses nicht in erster Linie was sie sind, sondern was man mit ihnen machen kann. Überlegen wir uns also, was man mit Strichlisten anstellen kann. Hier ist ein Vorschlag:

A1
Von zwei beliebig gegebenen Strichlisten kann man zweifelsfrei feststellen, ob sie gleich sind. Ob sie sich also, was die Gesamtheit ihrer Striche angeht, nicht unterscheiden. Zum Beispiel sind '//' und '//' gleich, während '//' und '///' ungleich sind. Wenn eine Strichliste der Strichliste '' gleich ist, nennt man sie leer.
A2
Zu jeder gegebenen Strichliste kann man eine neue Strichliste bilden, die eine getreue Kopie derselben ist - die also im obigen Sinne dem Original gleich ist, aber einen anderen Teil unserer Schreibfläche einnimmt, und daher sowohl von ihrem Original, als auch jeder anderen zuvor erzeugten Strichliste unterschieden werden kann. Zum Beispiel ist für gegebenes '///' eine Kopie gegeben durch '///'.
A3
Jede gegebene nicht leere Strichliste kann man dadurch verändern, dass man den letzten Strich weglässt. Das Ergebnis dieser Veränderung ist wieder eine Strichliste. Zum Beispiel ist für gegebenes '/' die veränderte Strichliste gegeben durch ''.
A4
Jede gegebene Strichliste kann man dadurch verändern, dass man eine weitere gegebene Strichliste am Ende anhängt. Das Ergebnis dieser Veränderung ist wieder eine Strichliste. Zum Beispiel wird '//' durch Anhängen von '//' verändert zu '////'. Hätten wir nicht die einschließenden Anführungszeichen zum Bestandteil unserer Schreibweise gemacht, wäre das Anhängen einfach durch lückenloses Nebeneinander-Schreiben der gegebenen Strichlisten zu erreichen. So aber muss man noch das Verschwinden des inneren Paars von Anführungszeichen vereinbaren, damit aus nebeneinander geschriebenem '//''//' endlich '////' wird.
Es leuchtet sicher ein, dass von einem 'mechanischer Umgang mit Zeichenketten' nicht die Rede sein kann, wenn diese elementaren 'Möglichkeiten des Umgangs', oder 'Methoden' weder direkt zur Verfügung stehen, noch mit Hilfe verfügbarer Methoden nachgebildet werden können.

Damit haben wir alles beisammen was man braucht um das mathematische Konzept Zahl genau festzulegen. Genauer, sprechen wir hier von den natürlichen Zahlen, unter denen man wohl, als der Begriff gebildet wurde, die Zahlen 1,2,3,... verstand, aber zu denen man heute aus vielerlei Gründen die 'nur halb natürliche' Zahl 0 meist hinzurechnet.

Hier ist nun die Definition der natürlichen Zahlen und ihrer Rechenoperationen Addition und Multiplikation:

B1
Jede Strichliste ist eine natürliche Zahl und jede natürliche Zahl ist eine Strichliste.
B2
Für zwei beliebige natürliche Zahlen definiert man ihre Summe als die Zahl, die durch Anhängen der zweiten Strichliste an die erste entsteht.
B3
Für zwei beliebige natürliche Zahlen m und n definieren wir ihr Produkt m⋅n als die Zahl p, die durch folgenden Vorgang entsteht:
  1. Anfangs setzen wir p='' und bilden eine Kopie k von n.
  2. Solange k nicht leer ist, hängen wir m an p an und lassen in k einen Strich weg.
  3. Wenn k leer ist, sind wir fertig und p ist das Produkt von m und n.
Führen wir der Deutlichkeit halber die Rechnung m⋅n = '///'⋅'//' nach der Vorschrift B3 durch:

Anfang: p='', k = '//'.

1. Schritt (weil k nicht leer) p='///', k='/'.

2. Schritt (weil k = '/', also nicht leer) p='//////', k=''.

Kein weiterer Schritt weil k=''. Das Resultat ist der letzte Wert von p, also '//////'.

Damit ist unser stummer Eilmarsch überstanden. Jetzt setzen wir uns ans eilig entfachte Lagerfeuer und diskutieren unsere Beobachtungen. - "So geht das aber nicht" meldet sich Peter (woher kommt er nur, mir war gar nicht aufgefallen, dass er mit dabei ist; auch meinen alten Mathelehrer KS entdecke ich mit Erstaunen). "Man kann doch nicht einfach sagen: eine Strichliste ist eine Zahl. Vielleicht wäre noch vertretbar zu sagen: 'stellt eine Zahl dar', oder eine Strichliste 'ist ein Zahlzeichen', oder so etwas ähnliches." - "Gut, gut" kommt mir da KS zuvor, "nach meinem Gefühl kann man das - sozusagen als Fachsprache - gelten lassen. Kürzer und verständlicher wäre das oben Gesagte in Peters korrekter Sprechweise sicher nicht. Meine Toleranz gegen die sprachliche Laxheit unseres Expeditionsleiters (ach ja, das bin ich also doch, Verantwortung inklusive) kommt daher, dass ich weiß, dass man viele andere Darstellungen der natürlichen Zahlen ..." - "Oh, bitte nicht" bremse ich, "diese Pointe wollte ich für später aufheben." - "Einverstanden," sagt KS, "dann also nur eine kurze Bemerkung direkt zur Sache: In der auf John v. Neumann zurückgehenden Zahlenreihe der Mengenlehre schreibt man für

'','/','//','///', …

viel einfacher (sarkastischer Unterton)

∅, {∅}, {∅,{∅}}, {∅,{∅},{∅,{∅}}}, …

Dabei weiß ich nicht, ob das Wichtigste schon gesagt wurde: Dass wir nämlich alle gewohnt sind dafür 0, 1, 2, 3, … zu schreiben." Ach, da war sie wieder, diese häufigste Fehlleistung beim Erklären zusammengesetzter Sachverhalte: mindestens einen fürs Verständnis unerlässlichen Punkt auszulassen, weil er dem Erklärenden im gegebenen Zusammenhang selbstverständlich zu sein scheint. Die Bierdeckel-Vorgeschichte, so tröste ich mich, könnte diesen Punkt aber doch geklärt haben.

"Eigentlich sieht unsere kleine Welt der Strichzahlen ganz stimmig aus," meint Lilly, eine blitzgescheite Biologin (mit einigen Semestern Mathematik, so nebenbei), die nun ihr Geld mit Software-Entwicklung verdient, "aber ich bin erst zufrieden wenn ich sie getestet habe". "Getestet? Was verstehen Sie denn darunter?" (KS redet uns alle mit 'Sie' an). "Nun - Sie werden denken, das ist eine Berufskrankheit - für mich ist ein Gedankengebäude erst dann gesichert, wenn man es programmieren kann und es als laufendes Programm wirklich das liefert, weswegen das Gedankengebäude errichtet wurde". - "Für mich ist das ein ziemlich fremdartiger Gedanke." räumt KS ein "Rechnen Computer nicht von vornherein mit Zahlen? Wie soll man ihnen dann unsere Strichlisten-Mathematik beibringen?" "Die Zeiten sind erfreulicherweise schon seit Jahrzehnten vorbei" entgegnet Lilly freundlich, "heute kann man in allen ernsthaften Programmiersprachen mit Zeichenketten so umgehen wie mit Zahlen und all den Tausenden anderer Objekte, die man sich nach ein paar Jahren Programmierpraxis selbst geschaffen hat". "Da ist ja Einiges an mir vorbeigelaufen" wundert sich KS, "es würde mich interessieren wie das genau aussieht." "Kein Problem", sagt Lilly und holt ihren Notebook-Computer aus dem Rucksack, "da ist alles was wir brauchen - in einer halben Stunde sollten wir wissen ob die Strichlisten-Mathematik funktioniert." (Als Industrie-Profi macht Lilly für alles einen Zeitplan. Weil sie - anders als so mancher Industrie-Profi - sich jedesmal Rechenschaft darüber gibt inwieweit sie den Plan hat einhalten können, liegt sie mit ihren Schätzungen meist erstaunlich richtig.) Wir haben uns alle um Lilly und ihren kleinen Computer gedrängt auf dessen Bildschirm wir die Schreibfläche eines ganz normal aussehenden Schreibprogramms sehen. Eine neue Textdatei mit Namen ns.rb hat Lilly schon erzeugt. "Das 'n' steht für 'natürliche Zahlen', das 's' für 'Strichlisten' und '.rb' für das Programm Ruby , das ich mit der F5-Taste starten werde sobald ich in die Datei etwas geschrieben habe, was Ruby versteht" erläutert Lilly die Situation. (Nicht nur programmierende Biologinnen sprechen von ihren Programmier-Hilfsmitteln wie von Menschen; man sieht Peter an, dass ihm solcher Sprachgebrauch zuwider ist.) Lilly schreibt:

puts '////'; puts ''; puts '///'

und drückt auf die F5-Taste. Darauf teilt sich das Schreibfenster und auf der rechten Seite erscheint die Schrift:

>ruby ns.rb
////

///
>Exit code: 0

"Das bedeutet" -Lilly nimmt einen hymnischen Tonfall an - "Ich, Ruby, Geisteskind des Yukihiro Matsumoto, habe deine Datei ns.rb verarbeitet. Deren Anweisungen gewissenhaft ausführend habe ich die folgenden drei Zeilen geschrieben. Dann habe ich die Arbeit beendet, ohne besondere Vorkommnisse, wie die Zahl 0 am Ende sagt". - "Was war eigentlich die Aufgabe?" fragt KS um Orientierung ringend. - "Das hatte ich ja noch gar nicht gesagt." beruhigt Lilly "Was ich zeigen wollte ist, wie man mit Ruby Strich-listig redet. Das komische Wort 'puts' steht für 'put string', also 'schreibe eine Zeichenkette'. Weil der Programmlauf keinen Fehler und keine Warnung ergeben hat, ist klar, dass meine Eingabe '////' als Zeichenkette verstanden wurde. Genau gesagt wurden die Striche zwischen den Gänsefüßchen als Zeichenkette gelesen und ausgegeben. So hatten wir das ja auch gemeint, als wir den Gebrauch der Gänsefüßchen vereinbart haben. Auf der folgenden Zeile sehen wir klar und deutlich die leere Zeichenkette geschrieben, genau wie es sein sollte." Peter lächelt wieder - er liebt Ironie. "Jetzt können wir uns die verlangten Methoden zum 'mechanischen Umgang mit Zeichenketten' ansehen:

puts '//'=='//'; puts '//'=='///'

erzeugt die neuen Ausgabezeilen

true
false

Dass Ruby Englisch spricht wissen wir ja schon. Wir haben also die Antworten 'wahr' und 'falsch' erhalten, und zwar auf die Fragen ob die Zeichenketten '//' und '//' gleich sind (Antwort 'wahr') und ob die Zeichenketten '//' und '///' gleich sind (Antwort 'falsch'). Überraschend ist vielleicht, dass anstelle des Zeichens '=' das Zeichen '==' als Gleichheitszeichen verwendet wird. Der Grund dafür ist natürlich, dass '=' für einen anderen Zweck gebraucht wird, den wir sehen werden, wenn wir zum zweiten Punkt unserer Liste kommen: Für die Herstellung einer Kopie sorgt die Methode clone. Die Eingabe

u='///'; v=u.clone; puts u; puts v; puts u==v
puts u.object_id == v.object_id

liefert die Ausgabezeilen

///
///
true
false

also ist die Kopie v tatsächlich dem Original u gleich, steht aber an einer anderen Stelle im Rechenspeicher (die hier durch die Methode object_id gegeben wird) was zur Folge hat, dass eine Veränderung von u, die Kopie v unverändert lässt. Der Rechenspeicher ist für Ruby beim Rechnen das Gegenstück zu unserer Schreibfläche. Nur für Eingabe und Ausgabe verwendet Ruby auch den Graphikspeicher und damit die für Menschen zugängliche Schreibfläche des Computers. Das Wort 'clone' legt das biologische Gleichnis nahe: u und v kann man sich als Lebewesen vorstellen, die Gesamtheit ihrer Striche als ihr Erbgut, womit dann u und v also über das gleiche Erbgut verfügen, aber trotzdem verschiedene Individuen sind. Bei unseren vorgestellten Lebewesen ist die Biotechnologie aber schon viel weiter als in der Wirklichkeit: Das Erbgut jedes Individuums kann nämlich in vivo mit einer Vielzahl von Methoden verändert werden von denen unsere Liste im dritten Punkt ja eine benennt: Abtrennen des letzten Gens, um im Gleichnis zu bleiben. Ruby hat dafür die Methode chop!:

u='/'; puts u.chop!

liefert eine Leerzeile als Ausgabe. Das war zu erwarten, weil das Ergebnis ja die leere Zeichenkette ist. Und

u='/////'; puts u.chop!.chop!.chop!

liefert

//

Endlich noch das Anhängen von Strichlisten: Die Eingabe

u='//'; v='//'; u << v; puts u; puts v

liefert

//// //

also die korrekt aneinander gehängte Strichliste für u und keine Änderung für v. Somit sind nach der ' << '-Operation u und v nicht mehr gleich. Es liegt nahe von << und chop! als verändernden Methoden zu sprechen, und von clone als einer erzeugenden Methode.

Damit haben wir gesehen, dass Ruby die Methoden A1 - A4 zum Umgang mit Strichlisten beherrscht. Ich werde sie jetzt schnell so zusammenbauen, wie es unsere Definitionen B2 und B3 von Addition und Multiplikation verlangen. Dazu brauche ich aber ein paar Minuten Konzentration." - "Entschuldigung, ich brauche vorher noch einen Tipp zum Verständnis der vielen Punkte in der Notation" wirft KS ein. - "Klar, das hätte ich sagen sollen: wo man in einer Mathe-Arbeit f(x) schreibt, schreibt man in einer modernen Programmiersprache (meist) x.f" - "Aha," bedankt sich KS "Postfix-Notation, mit Punkt als Trennzeichen, sehr praktisch ... , aber jetzt will ich Ihrer Konzentration nicht länger im Wege sein." So denken wir alle und blicken stumm ins Lagerfeuer und manchmal auf den Bildschirm von Lilly's Computer, der sich zügig mit Zeichen füllt. Nach guten zehn Minuten sieht er so aus:

# Hilfsfunktionen; x und y gehören zur Ruby-Klasse String
def a(x,y) # a wie Addition
   r=x.clone; r << y; r # x+y geht auch
end

def m(x,y) # m wie Multiplikation
   xc=x.clone; r=''; r << y while xc.chop!; r
end

def i(x) # Wert ist eine Ruby-Zahl
   x.length
end

ZeroString=''; OneString='/'

class N
# Natürliche Zahlen als Zeichenketten, hier Strichlisten.
# Für 1,0-Folgen anstelle von Strichlisten (Binärzahlen)
# sind nur die vorausgehenden Definitionen zu ändern

def initialize(s); @e=s.clone; end
# @e wie 'Erbgut', s und @e gehören zur Ruby-Klasse String

def e; @e; end
# macht @e zugänglich

def ==(n); @e.eql?(n.e); end
# n gehört zur Klasse N
# benützt Prüfung von Zeichenketten auf Gleichheit

def clone; N.new(@e); end
# 'new' benützt 'initialize' und damit die Methode 'clone'
# für Zeichenketten

def N.zero; N.new(ZeroString); end
# Strichlisten-freie Form der 0

def N.one; N.new(OneString); end
# Strichlisten-freie Form der 1

def +(n) N.new(a(@e,n.e));end
# Addieren, n gehört zur Klasse N

def *(n) N.new(m(@e,n.e));end
# Multiplizieren, n gehört zur Klasse N

def to_s; i(@e).to_s; end
# Wandlung in Zeichenkette von Dezimalziffern , nur für
# Ausgabe in vertrauter Form nötig: wer kann schon
# Millionen Striche Zählen

end # class N

# Sessa Aufgabe bis Feld zMax
z0=N.zero; z1=N.one
z2=z1+z1; z3=z2+z1; z8=z2*z2*z2; z16=z8+z8; z24=z8*z3
z27=z24+z3; z64=z8*z8
# ein paar Strichzahlen erzeugen wir uns mit den Rechenoperationen
# + und * aus der 1
# zMax=16 # Wert für Tests während der Entwicklung
# zMax=z27 # höchster Wert ohne Speicherfehler
zMax=z64 # bis zu diesem Feld des Schachbretts gehen wir
sum=z0.clone; act=z1.clone; i=z0.clone; t0=Time.now #Startwerte
while i!=zMax # Die Schleife wird zMax mal durchlaufen
   i+=z1; sum+=act; act*=z2
   t1=Time.now; t=t1-t0
   $stdout << "Man braucht " << sum.to_s <<
   " Körner bis zum Feld " << i.to_s << "\n"
   $stdout << " Rechenzeit: " << t.to_s << " Sekunden" << "\n"
   $stdout.flush
end

- "Ich bin etwas weiter gekommen, als ich mir vorgenommen hatte, nämlich etwa dahin, wo Sherams Schatzmeister zu seinem König zum Rapport geholt worden sein dürfte: Für die Felder eins bis 27 brauchen wir 134 Millionen Weizenkörner, also etwa 13,4 Tonnen Weizen." "Eindrucksvoll, wirklich eindrucksvoll," unterbricht KS Lilly's Redefluss "aber ich kann mir noch kein Bild davon machen, was Sie hier gezaubert haben." "Wenn hier jemand gezaubert hat, dann der schon genannte Yukihiro Matsumoto, der Ruby so konstruiert hat, dass man damit so schnell, sicher, und - das werden jedenfalls Sie als Mathematiker nachfühlen können - vergnüglich arbeiten kann. Was auf dem Bildschirm zu sehen ist, ist dies: die Strichlisten-Mathematik habe ich für Ruby verständlich definiert als ein Gebilde 'class N', also etwa 'Klasse der Natürlichen Zahlen'. Und dann ein kurzes Programm geschrieben, das diese N-Zahlen zur Berechnung der Anzahl von Weizenkörnern in unserem Sessa-Problem verwendet." "Bei der Wahrheit bleiben!" warf Peter ein, "ich hab dir zugeschaut und gesehen wie du immer zwischen oben, also wohl deiner class N und unten, also wohl dem Sessa-Problem, hin und her gesprungen bist, die arme F5-Taste behämmert hast, vom der erscheinenden Anzeige sichtlich überrascht mal hier mal da Änderungen gemacht hast, und jetzt präsentierst du uns das Ganze so, als ob dich ein Herr Matsumoto sicher zum gewünschten Ergebnis geleitet hätte. Übrigens - wo steht überhaupt das Ergebnis?" "Das hab ich mir gemerkt und wieder gelöscht. Ein wenig Show muss sein - also drück' du auf die F5 Taste um uns das Ergebnis zu zeigen". Ruby beginnt mit rasender Geschwindigkeit den Bildschirm mit einer Liste vollzuschreiben:

>ruby ns.rb
...
Man braucht 15 Körner bis zum Feld 4
 Rechenzeit: 0.0 Sekunden
Man braucht 31 Körner bis zum Feld 5
 Rechenzeit: 0.0 Sekunden
Man braucht 63 Körner bis zum Feld 6
 Rechenzeit: 0.0 Sekunden
Man braucht 127 Körner bis zum Feld 7
 Rechenzeit: 0.0 Sekunden
...
und wird dann langsamer und langsamer
...
Man braucht 8388607 Körner bis zum Feld 23
 Rechenzeit: 5.796 Sekunden
Man braucht 16777215 Körner bis zum Feld 24
 Rechenzeit: 11.765 Sekunden
Man braucht 33554431 Körner bis zum Feld 25
 Rechenzeit: 23.718 Sekunden
Man braucht 67108863 Körner bis zum Feld 26
 Rechenzeit: 66.546 Sekunden
Man braucht 134217727 Körner bis zum Feld 27
 Rechenzeit: 178.75 Sekunden
ns.rb:6:in ` << ': failed to allocate memory (NoMemoryError)
from ns.rb:6:in `m'
from ns.rb:35:in `*'
from ns.rb:54
>Exit code: 1

- "Von Schritt zu Schritt hat sich die Rechenzeit etwa verdoppelt" kommentiert Lilly "aber beim letzten Schritt schon fast verdreifacht. Das kam natürlich vom knapp werdenden Speicherplatz. Zum Schluss ist der Speicherplatz völlig ausgegangen. Das ist kein Wunder, denn beim übernächsten Schritt hätte die Ergebnis-Strichliste schon mehr Striche, als mein kleiner Computer Speicherzellen."

"Trotz aller Computerei wissen wir also immer noch nicht, wie viele Weizenkörner Sessa sich gewünscht hat" kritisiert Peter. "Trotzdem ist die praktische Seite des Problems gelöst" stellt Lilly trocken fest. "Wie das?" kommt es aus unserer Runde. "Mit diesem Ergebnis in der Tasche würde ich mir zutrauen den König zu überzeugen, dass er Sessas Bitte nie erfüllen kann. Ich würde im vorsichtig suggerieren zu Sessa zu sprechen in der Art: 'Oh, weiser Sessa, bei mir hat es leider etwas lange gedauert bis ich die Weisheit hinter Deinem Wunsch erkannt habe; so bitte ich Dich jetzt das Unmögliche gegen das Mögliche zu tauschen und damit einverstanden zu sein, Dich an so vielen Tagen wie ich Köche beschäftige - es sind vierundsechzig - von mir zu einem Festmahl einladen zu lassen. Wir würden dann zu Deinen Ehren eine Partie Schach spielen und jeden Tag würde ein anderer Koch die Bereitung des Mahles leiten. Was hältst Du davon, mein Freund?'. Ich glaube diese Art sich aus der Affäre zu ziehen würde dem König gefallen, auch wenn sie der vermutlich asketischen Lebenshaltung des Weisen Sessa nicht optimal entsprechen würde." "Ganz schön raffiniert" kommentiert Peter, "aber als Lösung der praktischen Seite des Problems kann ich das akzeptieren".

"Eigentlich ging es ja gar nicht um Sessas Problem, als Sie ihren Computer ins Spiel brachten" wendet sich KS an Lilly, "sondern um die experimentelle Untersuchung unseres Ansatzes Zahlen als Strichlisten einzuführen. Was haben wir diesbezüglich gelernt?" "Gut dass Sie darauf zurückkommen - ich hätte das Wichtigste wiedermal vergessen, weil es mir in der kurzen Zeit der Beschäftigung damit selbstverständlich geworden ist. Also, der Test verlief ohne Überraschungen und mit positivem Ergebnis. "Note ?" fragt KS. "Ich würde sagen Eins mit Stern, aber in Ruby-Dingen bin ich nicht objektiv." "Als Chef-Skeptiker hätte ich gerne eine Begründung" meldet sich Peter "aber vorher wollte ich noch wissen wie deine Computerarbeit wirklich abgelaufen ist. Bisher hast du meine diesbezüglichen Beobachtungen nur mit der Erlaubnis beantwortet, die F5-Taste zu drücken." "Ich weiß nicht, ob das ein allgemein interessierendes Thema ist. Ist es etwas Bestimmtes, was du wissen willst?" antwortet Lilly zögerlich. "Ich bin eben neugierig, und die Art des Arbeitens kam mir sehr vertraut vor. So ähnlich mache ich es auch, wenn ich Russische Texte übersetze - was nun mal mein Job ist." "Oha, mit Übersetzten hat das viel zu tun" entgegnet Lilly nun wieder ganz lebendig, weil sie nicht länger das Gefühl hat über etwas zu sprechen, was niemanden interessiert. "Ich habe ein Mini-Software-Projekt im modernen Stil durchgezogen. Dazu habe ich den in allgemeinen Begriffen ja schon gut beschriebenen und durchdachten Problembereich 'Strichlisten-Mathematik' in die Form des Software-Konstrukts 'class' gebracht. 'Represent concepts as classes' ist der Rat von Bjarne Stroustrup, dem Entwickler der Programmiersprache C++, die ich genauso schätze wie Ruby. Bei einem echten Softwareprojekt hat man Dutzende bis Tausende von Klassen."

"Klassen - das klingt ja sehr nach Mengenlehre" wirft KS ein. "Dort ist ja 'Klasse' der Überbegriff und 'Mengen' sind solche Klassen, die selbst Elemente von Klassen sind. Hat das Software-Konstrukt class, wie Sie sagen, damit etwas zu tun?" "Zu tun, sicher, aber ich durchschaue nicht ganz wie. Ich habe mich immer darüber gewundert, dass ich nie etwas dazu geschrieben fand, obwohl sich die Frage doch jedem Programmierer stellt. In meinem Fach, der Zoologie, gibt es natürlich auch Klassen: z.B. die Klassen Fische, Lurche, Reptilien, Vögel, und Säugetiere. Daneben sollen sie auch in Schule, Gesellschaft, Sport, Handel, ... vorkommen. Der Zusammenhang des Software-Konstrukts class mit solchen klassifizierenden Begriffen, wird übrigens in der Informatik-Literatur regelmäßig zur Illustration herangezogen. Schließlich bedeutet ja das von Stroustrup verwendete Wort 'concept' gerade 'Begriff'.

Die Programmiersprachen in denen man Klassen definieren kann, nennt man heute allgemein 'Objekt-orientiert', weil man von dem was da in Klassen zusammengefasst ist als Objekten spricht. Vermutlich hat 'object-oriented' im Englischen nicht diesen Beigeschmack nach Werbeagentur wie ähnliche Bildungen im Deutschen, etwa 'unsere konsequent qualitäts-orientierte Firmenkultur'. Ruby und C++ sind damit Objekt-orientierte Sprachen oder kurz OO-Sprachen. Klassen in diesem Sinn wurden eingeführt von Ole-Johan Dahl und Kristen Nygaard in der Programmiersprache Simula 67 (1967) nach einer Vorstufe SIMULA I (1962-65). "Mich haben die zwei Klassen-Begriffe auch irritiert" melde ich mich zu Wort "und habe gefragt ob wir es hier mit einer Zwei-Klassen-Mathematik zu tun haben. Lange konnte ich gar nicht glauben, dass die Sache so einfach ist: Jetzt verbürge ich mich dafür, dass OO-Klassen auch Klassen im Sinn der Mathematik sind und dass das, was auf Englisch 'x is an instance of class X' heißt, und im Deutschen meist wiedergegeben wird als 'x ist eine Instanz der Klasse X' [obwohl sich das mit dem Bedeutungsumfang des Wortes 'Instanz' im Deutschen schlecht verträgt], mathematisch korrekt ausgedrückt ist durch 'x ist ein Element der Klasse X' geschrieben als 'x ∈ X'. Die Frage, ob eine OO-Klassen selbst wieder Element einer OO-Klassen sein kann, hängt von der Programmiersprache ab. In Ruby besteht diese Möglichkeit (Klassen können hier also auch Mengen sein), aber mir ist noch keine Situation untergekommen, bei der es sich angeboten hätte von dieser Möglichkeit Gebrauch zu machen. Es wird sich bestimmt Gelegenheit bieten auf diesen Punkt zurückzukommen. Jetzt bin ich erst mal gespannt auf die Fortsetzung von Lilly's Bericht."

"Also zurück zu meinem Mini-Projekt: Parallel zur Definition der Klasse habe ich ein Testprogramm geschrieben, so dass jeder Zuwachs bei der Klasse sich durch einen entsprechenden Zuwachs im Testprogramm gleich prüfen lässt. Bei so einfachen Sachen wie hier kann man natürlich auch mal einen Schritt beim Testen überspringen und so habe ich es einrichten können - das ist der Punkt, denn ich mir als kleinen Erfolg anrechne - gleich das Sessa-Problem als Testprogramm zu benützen." "Interessant", wirft Peter ein, "wie will man ein Problem als Test verwenden, wenn man seine Lösung nicht kennt?" "Indem man es in eines verwandelt, dessen Lösung man kennt. Genau das habe ich gemacht, indem ich mein Testprogramm nicht so geschrieben habe, wie es Sessas Problem nahelegt, nämlich stur und stumm von Schachfeld 1 bis 64, sondern mit einer frei wählbaren Nummer des letzten zu bearbeitenden Schachfelds und - darauf bin ich erst ganz zum Schluss gekommen - mit der Anzeige aller Zwischenergebnisse. Das lag für mich nahe, weil ich ja vorher wusste, dass in meinen Computer nicht 'so viele Striche hineinpassen' wie für 64 Felder herauskommen würden. Obwohl also naheliegend, hat dieser kleine Trick doch viel Ähnlichkeit mit den großen Tricks in der Mathematik und auch mit den Tricks, die man bei schwierigen Aufgaben im Beruf manchmal nötig hat. Sessas Problem kann im Kopf so mit der 'magischen Zahl' 64 verbunden sein, dass man eine geistige Fessel sprengen muss, um zu erkennen, dass man dasselbe Spiel mit jeder anderen Zahl auch machen kann, zum Beispiel mit vier Feldern 1+2+4+8=15." "Wenn ich mich recht erinnere, haben wir dafür schon in der Schule eine fertige Formel verwendet - war es nicht einfach 264, im Fall des vollen Schachbretts?" wirft Peter ein. "Genau genommen 264 - 1" weiß Lilly "das macht, wenn man statt 64 nur 4 verwendet schon einen Unterschied, eben den zwischen 16 und den gerade als richtig erkannten 15." "Jetzt fühle ich mich ganz komisch im Kopf." spricht Peter vor sich hin "Bisher hat mir unsere Reise von den Bierdeckeln bis zum Stress des Schatzmeisters ganz gut gefallen und sie schien eine gewisse innere Logik zu haben. Welchen Sinn hat das Ganze aber, wenn wir die Lösung doch schon kennen?" "Denk an Landaus Töchter!" versuche ich Peters trübe Gedanken zu zerstreuen. "Wer mit Formeln nach Schüler-Art umzugehen versteht, der muss nicht wirklich wissen, was er tut. Wir sind ja dabei zu erkunden, worauf man das Zahlen-Rechnen zuverlässig und funktionierend begründen kann. Dass unsere Schul-Rechenkunst im Prinzip ausreichen würde um die Sessa-Zahl zu berechnen war uns doch von vorn herein klar." "Also gut, wechsle ich wieder vom Sinnsucher zum Chef-Skeptiker. Lilly, du wolltest noch erklären warum du mit deinem Programmiertest so zufrieden warst." "Weil er gezeigt hat, dass man die Natürlichen Zahlen als Ruby-Klasse so programmieren kann, dass nichts von Ruby's eingebautem 'Zahlenverständnis' benützt wird, sondern allein unsere Methoden A1 - A4 zum Umgang mit Zeichenketten und die einfachsten Hilfsmittel zum Formulieren von Rechenvorschriften, nämlich der Gebrauch von Variablen, die Wertzuweisung, und die 'while-Schleife'. Diese Hilfsmittel wurden auch in unserer weniger formgebundenen Erklärung B3 benötigt. Dass das gehen würde, davon war ich schon vorher überzeugt, aber dass das Ergebnis so schön übersichtlich sein würde, das hat mich doch etwas überrascht." "Nun, übersichtlich schon, aber so ganz durchsichtig ist mir die Sache noch nicht" wirft KS ein "es wäre schade, wenn ich auf meine alten Tage noch Ruby lernen müsste, um den Anschluss an Ihre Erkenntnisse halten zu können." "Geht mir auch so ... ", "Genau", ... kommt es aus unserer Runde. "Ich bin überzeugt, dass Ihnen Ruby-Lernen bald gefallen würde" wendet sich Lilly wieder an KS, "aber nötig sollte es nicht sein. In den wichtigsten Teilen ist Ruby einfach Mathematik. Ich glaube, wir sollten uns einfach die zwei wichtigsten Stellen in unserem Programm genauer ansehen, dann wird die einfache Logik dahinter erkennbar. Die wichtigsten Stellen sind natürlich die, mit denen die Multiplikation von N-Zahlen definiert wird. Die Definition der Addition ist etwas zu simpel. Es geht also um die Definition

def m(x,y) # m wie Multiplikation
   xc=x.clone; r=''; r << y while xc.chop!; r
end

deren einziger Sinn es ist in der Form von

class N
...
def *(n) N.new(m(@e,n.e));end
...
end

die Bedeutung des Multiplikationszeichen '*' für N-Zahlen zu erklären." "Durchaus übersichtlich, aber immer noch nicht durchsichtig" kennzeichnet KS seinen Eindruck. "Bald auch durchsichtig" ermuntert Lilly "Das Wort 'def' kündigt an: jetzt kommt die Definition einer Funktion. Dann kommt der Name der Funktion, also 'm'. Meine schreibfreudigen Kollegen würden statt meines 'm' Bandwürmer wie 'productInClassN' oder 'multiplication' wählen, bei denen das Wort 'Name' wohl weniger verwunderlich wäre. Mir kommen solche 'sprechenden Namen' albern vor, weil sie jedes nicht ganz triviale Formelgebilde so aufblähen, dass man es nicht mehr mit einem Blick erfassen kann. So viel zum Namen 'm' der zu definierenden Funktion. Dann kommt in Klammern eine Liste von Funktionsargumenten bei uns sind die Argumente also x und y. Diese haben damit Namen, nämlich 'x' und 'y', aber von welcher Art sie sind (zu welcher Klasse sie gehören) geht aus dem Programmtext nicht hervor. Ich habe es (für Ruby unlesbar) als Erklärung für den Leser

#... x und y gehören zur Ruby-Klasse String

hinzugefügt. Meine Kollegen würden würden statt x und y natürlich xString und yString schreiben. Mit diesen Größen ist die Anfangslage beschrieben für einen Rechengang, der mit den folgenden Anweisungen (statements) beschrieben wird

1. xc=x.clone
2. r=''
3. r << y while xc.chop!
4. r

Beim Versuch ein Programm zu verstehen (vielleicht eines, das man selbst vor ein paar Wochen geschrieben hat) denkt man sich für die Argumente 'beliebige, aber feste Objekte der vorgesehenen Art - hier also beliebige, aber feste Zeichenketten x und y - und geht mit dieser Vorstellung den Programmtext Anweisung für Anweisung durch, beobachtet das Kommen und Gehen von allerlei Hilfsgrößen und schließlich das Entstehen eines Ergebnisses. Fangen wir also an mit der Anweisung

xc=x.clone

Hier wird also zu der als gegeben angesehenen Zeichenkette x eine Kopie erzeugt. Das kann ein zeitraubender und speicherfressender Vorgang sein (natürlich nur bei der Programmausführung; es schadet aber nichts sich diese Folgen beim Durchdenken des Programmtextes klar zu machen). Dieser Vorgang ist allein mit dem Ausdruck x.clone verbunden. Er würde also auch ablaufen, würde man im Programmtext den Teil 'xc=' weglassen. In allgemeinen Begriffen sagt man, dass mit diesem Teil ein neues Objekt erzeugt wird. Die materielle Auswirkung davon ist, dass ein Teil des Rechnerspeichers für dieses Objekt reserviert wird und dass die Speicherzellen dieses Teils mit Inhalten gefüllt werden, mit denen die Eigenschaften des Objekts beschrieben werden. Eine Eigenschaft, die in jedem Fall gespeichert wird, ist der Name einer Klasse zu der das Objekt gehört. (Jedes von Ruby definierte Objekt gehört zu einer Klasse und der einzige Weg neue Arten von Objekten selbst zu programmieren verlangt einen Klassennamen für diese Art festzulegen). Jeder für ein neues Objekt angelegte Speicherbereich wird durch eine Ortsangabe, seine Adresse, gekennzeichnet. Diesen Speicherbereich kann man mit den Lagerhallen einer Filiale einer Handelskette vergleichen. Seine Adresse entspricht dann der Postanschrift der Filiale und der Klassenname entspricht dem Namen der Handelskette. Damit im weiteren Rechengang ein neu geschaffenes Objekt verwendet werden kann, muss seine Adresse im Gedächtnis bleiben. Das besorgt der Teil 'xc=' unserer Anweisung. Seine Ausführung beschäftigt nur wenige Speicherzellen und den Zeitaufwand dafür können wir getrost ignorieren. In allgemeinen Begriffen ist xc eine Variable. Man sagt kurz: xc speichert die Adresse des Objekts das als Wert des Ausdrucks x.clone erhalten wurde. Interessant: wir müssen nicht sagen, dass xc eine Variable ist - das ergibt sich schon aus den Regeln der Sprache. Weil wir jetzt wissen, was eine Variable ist, gleich der Nachtrag: Auch unsere Funktionsargumente x und y sind auch Variablen (die Mathematiker sagen: eine Variable, mehrere Variablen). Beim Ausführen von x.clone wird bei der in x gespeicherten Adresse ein Element der Klasse String gefunden und - gesteuert von diesen Informationen - wird erkannt, welche Hebel in Bewegung zu setzen sind um die den Ausdruck x.clone zu berechnen. Würde bei der Adresse x etwa ein Element der von uns definierten Klasse N gefunden, so würde dies durch Anwendung der Methode clone der Klasse N (vierte Definition in class N) zu bewerkstelligen sein. Man kann zwei Sorten von Ausdrücken unterscheiden: erzeugende Ausdrücke, deren Sinn es ist ein neues Objekt zu erzeugen und verändernde Ausdrücke, deren Sinn es ist ein schon existierendes Objekt zu verändern. Jeder Ausdruck liefert mit seiner Ausführung eine Adresse, sozusagen als Ausführungsnachweis. Ich nenne diese Adresse hier den (Rückgabe-)Wert des Ausdrucks, obwohl das Wort auch oft für das Objekt gebraucht wird, das am adressierten Ort zu finden ist. Bei erzeugenden Ausdrücken (wie der eben betrachteten) ist der Rückgabewert die Adresse des neu geschaffenen Objekts. Bei verändernden Aktionen ist er meist die (unverändert gebliebene!) Adresse des geänderten Objekts. Dazu mehr, sobald uns eine solche Methode begegnet. Die nächste Anweisung folgt demselben Schema, ist aber viel einfacher:

r=''

Hier ist wieder '' das erzeugte Objekt, nämlich eine leere Zeichenkette, und r die Variable, die sich die Adresse dieser Zeichenkette merkt. Nun zur nächsten Anweisung:

r << y while xc.chop!

Dieser ist gleichbedeutend mit der dreizeiligen Schreibweise

while xc.chop!
   r << y
end

in der man nun in den 'Wirkungsbereich der While-Schleife' außer r << y noch weiter Terme hineinschreiben könnte, was manchmal nötig ist. Daher wollen wir uns die Anweisung in dieser Form ansehen. Die Bedeutung der ersten Zeile ist die: Erst wird xc.chop! ausgeführt. Hier haben wir keinen erzeugenden sondern eine verändernden Ausdruck (Darauf deutet der mit '!' endende Name hin). Wir haben die Methode chop! heute ja schon kennen gelernt und wissen, dass xc dadurch um das letzte Zeichen gekürzt wird.

Hier gibt es einen sehr interessanter Punkt, den ich vielleicht schon bei unserer ersten Begegnung mit chop! hätte hervorheben sollen und der eigentlich schon bei unseren Feststellungen A3 und A4 angebracht gewesen wäre. In der Mathematik findet man höchst selten, dass einer Variablen, wenn sie mal einen Wert erhalten hat, im Verlauf einer Rechnung oder eines Beweises später ein anderer Wert gegeben wird; beim Programmieren indessen ist das die Regel. Ruby kennt (wie C++ und Java) die verändernden Rechenoperationen += und *= (um die wichtigsten zu nennen). Nach Ausführung von x+=1 hat x einen um 1 größeren Wert als zuvor. Es hat also die Wirkung von x=x+1. Hat man in Ruby in einer Klasse die normalen (erzeugenden) Operationen + und * definiert (wie wir für class N), so sind += und *= gleich automatisch mitdefiniert. Ich habe diese Operationen in meinem Testprogramm ja auch verwendet. Das ist hier allerdings nicht annähernd so effektiv, als würde ich *= und += selber als verändernde Operationen ohne überflüssiges Kopieren von Daten definieren. Leider erlaubt Ruby das nicht (der Versuch die Operation *= zu definieren wird als Grammatikfehler eingestuft und abgelehnt). Aber niemand verbietet veränderndes Addieren und Multiplizieren unter anderem Namen zu definieren, etwa als a! und m!. Macht man das in nächstliegender Weise (so habe ich angefangen), und schreibt im Testprogramm

i.a!(z1); sum.a!(act); act.m!(z2)

statt

i+=z1; sum+=act; act*=z2

so kommt man bis zum Feld 28 bevor der Speicher erschöpft ist und braucht trotzdem ein Drittel weniger Rechenzeit als vorher bis Feld 27. Bis zum Feld 27 braucht man sogar nur 10 Sekunden. Mit dieser schnelleren Version habe ich angefangen - verwarf sie aber als 'zu wenig pädagogisch'. Ich war dann ziemlich erstaunt, dass die 'pädagogische Version', die ich vorgeführt habe, doch nicht so viel langsamer ist. Trotzdem zeigt das Beispiel deutlich, wie nützlich es ist Objekte verändern zu können (so zusagen Operation in vivo) ohne sie insgesamt auf neuem Speicherplatz neu anlegen zu müssen (was die erzeugenden Methoden tun). Die Gründe dafür existieren für die Mathematik nicht: hier macht es keinen Unterschied ob man eine Variable mehrfach x verwendet, oder aber eine nicht endende Folge x1, x2, x3, ... immer neuer Variablen verwendet. Das hat dazu geführt, dass man den Begriff 'Term' in der Mathematik nur so verwendet, dass die Objekte aus denen er gebildet ist, durch eben diese Termbildung nicht verändert werden. Betrachten wir etwa eine 'normale' Funktion f, so bezeichnet der Term f′ eine neue Funktion (die Ableitung von f, hier ist nicht wichtig was das genau ist) und wenn man nachher f(1) schreibt, ist mit f immer noch das alte f gemeint und nicht die Ableitung davon. In einem Programm kann es sehr nützlich sein, eine dem Ableitungsstrich entsprechende Operation so zu definieren, dass f durch Ausführen dieser Operation in seine Ableitung verwandelt wird (also f durch seine Ableitung ersetzt wird). Wird das unveränderte f später auch noch benötigt, kann man es sich ja mit einer Variablen merken: g=f. Ich glaube, dass es nur Gewohnheit ist, wenn uns der mathematische Termgebrauch verständlicher und klarer vorkommt. Allerdings gibt es auch die Anhänger des 'Funktionalen Programmierens', die auch beim Programmieren den mathematischen Termgebrauch favorisieren [Andrew Koenig, Barbara Moo: Ruminations on C++, Addison-Wesley 1997, p. 190]. Andererseits kommt der gängigste Gebrauch von Variablen in der Mathematik und besonders in der mathematischen Logik beim Programmieren überhaupt nicht vor: die 'Bindung von freien Variablen' durch die so genannten Quantoren ∀ und ∃.

Kehren wir nach dieser Abschweifung wieder zu unserer While-Schleife zurück und dort zu dem Ausdruch xc.chop!. Der Rückgabewert des Ausdrucks ist interessant: Ist xc nicht leer, so ist alles normal und der Rückgabewert ist, wie für verändernde Ausdrücke üblich, die Adresse von xc. Ist xc dagegen leer, so kann die chop!-Methode nicht in üblicher Weise ausgeführt werden. Die plausibelste Reaktionsweise ist xc nicht zu ändern und das Besondere der Situation durch den Rückgabewert zu signalisieren: Er ist dann die Adresse eines schon existierenden unveränderbaren Warnungs-Objekts mit Namen nil." - "Das ist ja interessant!" unterbricht KS "Das erinnert mich an eine alte Idee von mir, bei der ich mir nie klar wurde, ob ich sie nur als einen Jux oder als etwas Vernünftiges ansehen soll." - "Jux ist immer gut" meint Peter "ich bitte um einen diesbezüglichen Einschub und bin bereit Lilly's Gedankenfaden am nächsten Busch festzuknoten, damit wir ihn wieder aufnehmen können, wenn der Jux vorüber ist." Die Zustimmung zu Peters Vorschlag ist allgemein, und so sieht sich KS gedrängt von seiner Idee zu berichten: - "Wenn in der Schule das Dividieren behandelt wird, man also Terme der Form Zähler/Nenner betrachtet, stellt sich die Frage, wie man mit der Möglichkeit Nenner = 0 umgeht. Oft wird gesagt: 'Durch Null darf man nicht dividieren'. Das ist nach meinem Empfinden ein ziemlicher Stilbruch, denn die Mathematik ist ja keine moralische oder juristische Instanz, die sagt, was man darf und was nicht." - "Das hab ich aber anders in Erinnerung" wirft Peter ein "hieß es nicht immer, 'man darf nicht Äpfel zu Birnen addieren, man muss das Vorzeichen umkehren, wenn man einen Term von einer Gleichungsseite auf die andere bringt ..." - "Ich nehme an, heute können Sie sich den richtigen Reim darauf machen." - "Ich bin da nicht ganz sicher." - "Nun, im Sinnzusammenhang der Schule ist das ja ganz richtig. Unterstellen wir, ein Schüler will (muss ?) eine brauchbare Mathe-Note erreichen, dann darf er tatsächlich nicht 5a+8b=13 rechnen, und muss sich tatsächlich an die paar wenigen Rechenregeln halten. Aber im Sinnzusammenhang der Mathematik selbst sollte man das anders ausdrücken. In unserem Fall der Division durch Null ist eine Möglichkeit zu sagen: 'Das Ergebnis einer Division durch Null ist nicht definiert'. Dann ist die Division durch Null nicht ausdrücklich verboten, aber auch keine Klarheit darüber hergestellt was passiert, wenn man es versucht. Das zu bessern war meine Idee: Natürlich darf man durch Null dividieren, jedermann ist aufgefordert es zu probieren, er bekommt auch garantiert ein Ergebnis - schwarz auf weiß, getrost nach hause zu tragen - das Ergebnis lautet 'nix'." - "Hoffentlich sind Sie damit noch nicht fertig" kann Lilly lautes Mitdenken nicht unterdrücken, "denn noch hätten wir in meinen Augen keinen Clou: was ist schon der Unterschied zwischen '1/0 ist nicht definiert' und '1/0=nix'?" - "Richtig! Es muss noch etwas kommen. Nämlich die Festlegung, dass mit 'nix' gerechnet wird wie mit einer normalen Zahl und zwar mit der naheliegenden Regel, dass jede Rechenoperation den Wert nix liefert, wenn mindestens eine der Eingangsgrößen den Wert nix hat. Also a + nix = nix + a = nix, a⋅nix = nix⋅a = nix. Eine Relation wie a < b, von der man üblicherweise sagt, sie hätte den Wert wahr oder falsch (z.B. 3<5 = wahr, 5<3 = falsch) bekommt natürlich auch den Wert nix, wenn ein Operand den Wert nix hat. Also 5 < nix = nix. Damit kann man jede Rechnung zu Ende führen, die man nach dem alten Verbotsschild 'du darfst nicht' hätte abbrechen müssen, und die einen nach der Regel 'nicht definiert' ratlos gelassen hätte. Beim Endergebnis der Rechnung bekommt man allerdings die Quittung: es kann 'nix' lauten. Dieses Ergebnis kann man etwas positiver klingend so interpretieren: Der Rechengang zur Bestimmung einer anfangs unbekannten Größe x hat keine Information über den Wert von x erbracht." - "Das erinnert mich an Odysseus in der Höhle des Polyphem." unterbricht Peter begeistert "Als der Zyklop den Odysseus nach seinem Namen fragt, antwortet dieser 'Niemand'. Und als Polyphem dann dieses Wort wie den normalen Namen seines inzwischen handgreiflich gewordenen Zwangsgastes verwendet, erreicht er nicht die Hilfe seines Zyklopen-Clans, sondern nur mitleidiges Unverständnis. Erfolg also: nix!" - "Bleibt nur noch festzustellen: nix = nil" bemerkt Lilly beeindruckt. "Ihr 'nix' ist genau Ruby's nil und somit besser nicht als Jux aufzufassen, sondern als ein durchaus nützlicher Begriff. Der Mathematiker und Computerwissenschaftler Dana S. Scott hat dafür das Schlagwort 'objectifying the undefined' geprägt und mit diesem Konzept computerwissenschaftlicher Begriffssysteme ähnlich vereinfacht, wie man die klassische Analysis ('Infinitesimalrechnung') durch Einführung einer "Zahl" ∞ vereinfachen kann." - "Nil ist übrigens ein richtiges englisches Wort." füge ich an. "Es ist ein Hauptwort, gebildet aus dem lateinischen 'nihil', und bedeutet 'Nichts'. Also auch sprachlich weitgehende Übereinstimmung mit KS's nix. Bei der üblichen Darstellung reeller Zahlen gemäß dem IEEE Standard für Gleitkomma-Arithmetik gibt es übrigens auch eine Form von nix mit Namen NaN (not a number) und andere Pseudozahlen für Ausnahmen. Auch die Techniken zur Ausnahmebehandlung, die alle modernen Programmiersprachen bereitstellen, kann man als Weiterentwicklungen des nix-Gedankens ansehen."

"Kann ich Lilly's Gedankenfaden nun wieder los binden?" fragt Peter und erntet keinen Widerspruch. - "Wir waren also bei der Aktion xc.chop!" nimmt Lilly den Faden wieder auf. "Nachdem sie ausgeführt ist, wird der Rückgabewert geprüft. Weil vor dem Term ein while steht wird untersucht, ob der eben erhaltene Rückgabewert die Adresse des nil-Objects (oder des ebenfalls möglichen false-Objekts) ist. Trifft dies zu, werden r << y und end übersprungen und also mit der Bearbeitung des Ausdrucks r fortgefahren. Im anderen Fall, wird r << y ausgeführt (wodurch r verändert wird, eben durch Anhängen von y) und - das ist der Effekt des 'Schleifen-Befehls' while- die Bearbeitung springt zurück (in unserem Fall also in die Zeile zuvor und nicht, wie es ohne einen Schleifen-Befehl der Fall wäre, in die nächste Zeile. Es wird also wieder xc.chop! ausgeführt und geprüft ob der Rückgabewert davon eine normale Adresse ist, oder die von nil (false kann ja nicht auftreten). Abhängig davon wird dann wieder entweder zurückgesprungen, oder es wird die Schleife verlassen. Weil xc bei jedem dieser Rücksprünge um ein Zeichen gekürzt wird, muss der letzte Fall einmal eintreten. Der Rückgabewert der Anweisung r << y while xc.chop! ist stets die Adresse von nil, weil ja bei der letzten Action xc.chop! berechnet wird und r << y nicht mehr. Der Rückgabewert unserer Funktion m(x,y) soll natürlich die Adresse der durch mehrfaches Verlängern erhaltenen Zeichenkette r sein, dazu dient die letzte Anweisung r. Damit die Bedeutung der Funktion m erklärt. Über der detaillierten Erklärung der Programmlogik auf unterster Ebene dürfen wir nicht vergessen, dass das Ganze ziemlich einfach war: Wir haben an die leere Strichliste mehrfach y angehängt und zwar einmal für jeden Strich von x. Brauchen wir eine Pause?" - "Nicht unbedingt" meint KS " in meinem Kopf haben sich einige Halbklarheiten angesammelt, von denen ich hoffe, dass sie sich von selbst klären, wenn wir mit der ganzen Sache durch sind. Wenn wir also gesagt haben wie m*n berechnet wird, wenn m und n gegebene N-Zahlen sind." - "Mir geht es ähnlich" stimmt Peter ein, "allerdings mit Unklarheiten statt Halbklarheiten. Aber ich würde auch gern einen Blick auf das Ganze werfen. Wenn ich eine Ausstellung besuche, renne ich auch erst mal durch alle Säle. Ich weiß dann besser wo es sich lohnt genauer hinzusehen." - "Gut" sagt Lilly "rennen wir also auch noch durch den letzten Saal der Ruby-Ausstellung: Trifft Ruby auf einen Ausdruck a*b mit Variablen a und b, so wird bei der in a gespeicherten Adresse nachgefragt zu welcher Klasse das dort gespeicherte Objekt gehört. Ist die Auskunft 'Klasse N', so tritt

class N
...
def *(n) N.new(m(@e,n.e));end
# Multiplizieren, n gehört zur Klasse N
...
end

in Kraft. Dass das erste Argument (also a) diese Rolle des Pfadfinders spielt und nicht die zweite überrascht sicher nicht. Das Bemerkenswerte (in meinen Augen das Wichtigste in der ganzen Objekt-orientierten Begriffswelt) ist, dass es diese Pfadfinderrolle überhaupt gibt. Diese wird auch optisch deutlich, wenn man die gleichbedeutende systematische Schreibweise verwendet: a.*(b) für a*b. Hier ist das a auch optisch hervorgehoben, weil hinter ihm der Punkt steht. Das b dagegen steht als gewöhnliches Funktionsargument in Klammern. Die obige Definition von * ist dann so zu lesen: Aus den String-Objekten a.e und b.e bilden wir das String-Objekt p=m(a.e,b.e) und dann die N-Zahl N.new(p), die genau dieses String-Objekt als Erbgut hat. Dieses ist die neue, durch a*b erzeugte N-Zahl. Ein Problem gibt es, wenn in b die Adresse eines Objekts gespeichert das nicht zur Klasse N gehört (was auf einen Programmierfehler hindeutet). Ist allerdings für ein solches Irrläuferobjekt der Term b.e definiert und liefert ein String-Objekt, so wird beim Ausführen von a*b kein Fehler erkannt."

"Für Rechenvorschrift oder Rechengang sagt man oft Algorithmus" ergänze ich. "Das Wort entwickelte sich aus dem Namen eines Arabischen Gelehrten des neunten Jahrhunderts, der in unseren Buchstaben als al-Khowarizmi oder Alchwarazmi wiedergegeben wird, und drückt nur die Herkunft dieses Mannes aus einer Usbekischen Stadt ähnlichen Namens aus. Man kann unsere Definition der Rechenoperationen + und * als algorithmische Definitionen bezeichnen. Ein Algorithmus braucht etwas, was er verarbeitet. Bei unseren Zahlen ist es das, was Biologin Lilly treffend als deren Erbgut bezeichnet. Glücklicherweise hat dieses Erbgut als endliche Zeichenkette eine Struktur mit der Mathematik von allem Anfang an umgehen muss und umgehen kann. Die in der Mathematik als Grundlage so beliebte 'Prädikatenlogik erster Stufe' verzichtet darauf, die Objekte ihres Interesses mit 'Erbgut' auszustatten, und verliert dadurch die Fähigkeit für die Natürlichen Zahlen eine Definition in ihrem Rahmen zu geben. Der Umstand, dass Zahlen dann trotzdem gleich am Anfang verwendet werden, etwa zum Nummerieren von Variablen oder zur Angabe der Länge von Argumentlisten, aber erst viel später im Rahmen einer 'Prädikatenlogik zweiter Stufe' eingeführt werden, ist dem glatten Verständnis der Sache nicht dienlich. Aus der geschichtlichen Entwicklung ist eine solche Misch-Konstruktion zwar gut verständlich, aber sich dauerhaft damit zufrieden zu geben entspricht nicht den Idealen der Wissenschaft." - "Diesen Schuh brauchen sich die großen Logiker und Mathematiker wohl nicht anziehen," wiegelt KS ab "aber mich hat auch schon manches angesehene Lehrbuch mit offensichtlichen Ungereimtheiten auf die Palme gebracht. Vielleicht sollten wir unsere Erkundung fortsetzen. Dann sammeln wir Erfahrungen und können über die gerade etwas hart angegangene Prädikatenlogik fundierter sprechen.

Ich würde gerne noch auf Peters anfänglichen Protest gegen unsere Feststellung B1 zurückkommen: in welchem Sinne sind unsere N-Zahlen Strichlisten, oder werden sie nicht eher aus Strichlisten gebildet?" - "Genau genommen natürlich letzteres, aber mit dem Sein liegt man auch nicht ganz falsch; mit Blick auf das Wesentliche vielleicht sogar richtiger." - "Das ist genau die Klarheit, die man von Mathematikern erwartet" frozzelt Peter. - "Klarheit braucht eben ein paar Sätze mehr: Wir haben drei Methoden um eine N-Zahl in ein Programm einzuführen in dem es noch keine N-Zahlen gibt:

n=N.zero
n=N.one
n=N.new('////')

In der dritten Zeile ist die Zahl der Striche natürlich nur ein Beispiel. Sind in einem Programm schon Zahlen eingeführt, so können auch die Rechenoperationen dazu dienen neue zu erzeugen: zum Beispiel

m=(n+n+n)*(n+n)+n

Wir kennen das von meinem Testprogramm. Dort wurde die Erzeugungsart mit Strichen übrigens überhaupt nicht verwendet. Allerdings verwenden die 'strichlosen' Methoden zero und one die Striche intern in den Definitionen ZeroString=''; OneString='/'. Wenn man auf sie verzichten kann, ist Erzeugung von N-Zahlen aus Strichlisten sicher nicht das Wesentliche. Wesentlich ist, dass die Erbgut-Variable @e eine Strichliste ist. Im Allgemeinen hat man mehrere solche Erbgut-Variablen. Wären wir schon bei der analytischen Geometrie, könnten wir zum Beispiel die Klasse Kreis definieren mit Erbgut-Variablen @x, @y, @r (dass der Variablen-Name mit @ beginnt ist Ruby-Vorschrift). Nun ist 'Erbgut' in diesem Zusammenhang nur meine Privatbenennung - für Kreise kommt sie mir selbst nicht sehr passend vor. Im Englischen sagt man meist 'instance variables', weil man ein Objekt das zu einer Klasse X gehört (ein Element von X ist) als 'instance of X' bezeichnet. Man kann sich von Ruby die instance variables jeder Klasse anzeigen lassen:

n=N.one; puts n.instance_variables → @e

(Hier - und im Folgenden oft - steht die Ausgabezeile platzsparend hinter dem Pfeilsymbol.) Weil die Werte der instance variables in gängigen Begriffen den Zustand (state) des Objekts festlegen, ist Zustandsvariable die passende deutsche Bezeichnung für 'instance variable'. Somit können wir sagen: N ist eine Klasse, deren Elemente Objekte sind, deren Zustand durch eine Strichliste festgelegt ist. Statt 'das Objekt x ist Element der Klasse X' ist auch üblich zu sagen 'x ist ein X-Objekt' oder 'das Objekt x ist vom Typ X'. Allerdings gibt es Programmiersprachen (z. B. C++), für die Typ und Klassenzugehörigkeit verschiedene Begriffe sind. Aus der aktuellen Definition der Programmiersprache Ruby ist der Begriff 'type' verbannt, so dass es keinen Fehler erzeugen kann ihn im obigen Sinn zu verwenden. Allerdings ist in der Ruby-Gemeinde eine informelle Typ-Definition beliebt, die unter dem Schlagwort 'dug-typing' läuft, und derzufolge Elemente verschiedener Klassen den selben Verhaltenstyp haben können.

"Auf eines bin ich jetzt gespannt" meldet sich Peter zu Wort "sind wir jetzt schon schlauer als Landaus Töchter, wissen wir also schon warum für N-Zahlen immer m*n = n*m gilt?" - "Das ist nicht nur im Sinne der Redensart 'eine gute Frage' sondern auch professionell formuliert." muss ich meinen einstigen Nachhilfeschüler loben. "Der Bezug auf die Art der Objekte, hier N-Zahlen, den du eingeflochten hast, ist natürlich wichtig. Wäre er nicht gegeben, wüsste man nicht, was das *-Zeichen bedeuten soll. In der Mathematik sind viele Multiplikations-Operationen in Gebrauch und nicht alle haben die Eigenschaft, dass die Reihenfolge der Faktoren keinen Einfluss auf das Ergebnis hat. Das Produkt von Matrizen, das uns sicher noch irgendwo begegnen wird, ist ein Beispiel dafür." - "In gängiger Redeweise fragen wir also, ob unsere Multiplikation von N-Zahlen 'kommutativ' ist, oder - wie man auch sagt - dem Kommutativgesetz genügt" ergänzt KS und fügt hinzu: "Ich würde allerdings vorschlagen als Vorübung mit dem Kommutativgesetz der Addition anzufangen." - "Als Vorübung OK, aber die eigentliche Aufgabe ist die Multiplikation. Wahrscheinlich hat sich Herr Landau etwas dabei gedacht" ist Peter nur halb zufrieden. - "Das ist ja peinlich" sage ich "ich wollte zum Kommutativgesetz der Addition eigentlich nur sagen, dass es selbstverständlich ist." - "Selbstverständlich? Wirklich?" zweifelt Lilly, klappt ihren Computer wieder auf und rechnet

m=N.new('////'); n=N.new('//////'); puts m+n == n+m →true

"Es werden hier also einmal eine Liste von sechs Strichen an eine mit vier angehängt (m+n) und das andere mal eine Liste von vier Strichen an eine mit mit sechs Strichen (n+m). Die Frage ob die beiden Ergebnisse gleich sind, wird mit ja beantwortet. Das überrascht ja nicht, aber wenn wir es für selbstverständlich ansehen wollen, kann es nichts schaden, wenn wir das, was wir da von selbst verstehen, wenigstens genau benennen. Jedenfalls - das ist wichtig - laufen beim Berechnen von n + m und m + n im Rechner verschiedene Vorgänge ab. Wäre das Füllen einer Seicherzelle von einem Lichtblitz begleitet, den man unter einem Mikroskop sehen könnte, so würde uns zwischen dem Blitzlichtgewitter bei der Berechnungen von m+n und dem bei n+m vermutlich gar keine Ähnlichkeit auffallen." - "Das ist ja ein ganz neuer Aspekt" ist Peter ganz begeistert "wenn ich dem Computer irgendeinen Ruby-Term zu berechnen gebe, löse ich damit ein Blitzlichtgewitter aus?" - "Das war nur ein Gleichnis von Lilly" muss ich erklären "Licht ist, soweit ich weiß, dabei kaum im Spiel, aber Wärmestrahlung durch Erhitzung, Radiowellen durch die heftig beschleunigten und abgebremsten elektrischen Ladungen - auf alle Fälle ein interessantes Spektakel. Allerdings läuft das so rasch ab, dass es der menschlichen Beobachtung nur durch die Vermittlung von registrierenden Messgeräten zugänglich ist. Dass man mit einer Berechnung einen Vorgang auslöst, ist ja eigentlich selbstverständlich: irgendwoher muss das Ergebnis ja kommen und die Anzeige des Ergebnisses ist ja auch ein Vorgang, den man ganz offensichtlich mit der Berechnung ausgelöst hat." - "Ja schon, aber die Vorstellung von dem Spektakel im Inneren finde ich viel interessanter. Wie ist das eigentlich: passiert im Computer immer genau dasselbe, wenn ich dasselbe Ruby-Programm mit der F5-Taste starte?" - "Keineswegs. Was tatsächlich passiert hängt davon ab, welche Speicherzellen durch andere Programme belegt werden. Zwischen dem Programmtext in unser Textdatei ns.rb und dem Spektakel im Inneren des Computers liegen ja noch das Programm Ruby (dessen Quelltext in der Programmiersprache C geschrieben ist) und das Betriebssystem des Computers (auch ein Programm!). Das letztere verteilt den Speicherplatz auf Anforderungen von vielen Seiten, nicht nur von Ruby. Das Rechenergebnis, wenn es zum ordnungsgemäßen Ende der Rechnung kommt, ist allerdings unabhängig von diesen wechselnden Begleitumständen, die Rechenzeit kann allerdings empfindlich von ihnen abhängen." - "Das hatte ich mir noch gar nie so klar gemacht" will Lilly offenbar ein Aha-Erlebnis mitteilen. "Der durch meine Datei ns.rb und die F5-Taste in Auftrag gegebene Rechen-Prozess arbeitet sich also durch (Speicher-)Raum und (Rechen-)Zeit in Konkurrenz mit anderen Interessenten für diese Ressourcen, kann dabei auf der Strecke bleiben, oder aber zum Ziel gelangen, schneller oder langsamer, je nach den Umständen. Das Ganze hat also eine gewisse Analogie zum Überlebenskampf eines Lebewesens. Ein Tier hat durch seine genetisch fixierte Instinktausstattung seine Leitlinien, die man als ein Programm mit dem Ziel der Fortpflanzung ansehen kann. Wenn das Programm sein Ziel erreicht (was allerdings etwa im Insektenreich nur bei einem verschwindend kleinen Teil einer Population der Fall ist) ist das Ergebnis weitgehend unabhängig von den wechselvollen Umständen längs des Lebenswegs des Tieres. Wenn man von einem Virus spricht, der den Computer infiziert hat, dann hat dieser eigentlich einen der vielen Prozesse, die im Computer auf ihrem Lebensweg sind, befallen und gezwungen sein Verhalten zu ändern."

"Nun aber zurück zum Kommutativgesetz der Addition" dränge ich. "Ich hatte dabei nicht an die unübersichtlichen Verhältnisse in einem tatsächlichen Computer gedacht. Eher an einen idealen Computer oder, besser, an ein kariertes Blatt Papier. Schreiben wir hier jeden unserer Striche in sein eigenes Kästchen und Striche einer Strichliste selbstverständlich ohne leere Kästchen dazwischen in eine Kästchenzeile. Schreiben wir nun den Zustand von m+n nach B2 in die erste Zeile unseres Blattes. Wir schreiben also m (vier Striche) beginnend mit dem ersten Kästchen der ersten Zeile und n (sechs Striche), beginnend mit dem ersten dann noch freien Kästchen dieser Zeile. Dann schreiben wir n+m nach demselben Rezept in die zweite Zeile: erst die sechs Striche (natürlich beginnend mit dem ersten Kästchen der zweiten Zeile), dann die vier, beginnend mit dem ersten nun freien Kästchen der zweiten Zeile. Die beiden Zeilen die so entstehen sind 'deckungsgleich': verschiebt man die erste als Ganzes um ein Kästchen nach unten, so hat man dort das, was in der zweite Zeile ohnehin stand. Das ist aber eine Beobachtung und keine Begründung. Genauso wie im komplizierten Computer, läuft also beim Schreiben der beiden Zeilen jeweils ein anderer Vorgang ab - die Nahtstelle zwischen den zwei aneinander zu hängenden Strichlisten liegt jeweils an anderer Stelle. Worauf beruht nun unsere Überzeugung, dass auch für beliebig lange Strichlisten die beiden entstehenden zwei Zeilen immer deckungsgleich sind?" - "Ich weiß nicht, ob ich diese Überzeugung habe" grübelt Peter. "Schlimmer noch, ich glaube, dass es gar keinen Unterschied macht ob das mit den zwei Zeilen immer so ist, oder ob bei ganz astronomisch langen Strichlisten mit denen nie ein Mensch zu tun hat kleine Unterschiede auftreten. Auftreten wäre dann ja schon wieder falsch. Eben gerade nicht auftreten, weil so lange Strichlisten nicht auftreten." - "Die Wertung solcher Abweichungen jenseits aller Erfahrungsmöglichkeiten ist eher eine Geschmacksfrage. Aber wir sollten doch Gründe dafür vorbringen können, dass es für Strichlisten die man in einem Computer (oder einem Verbund von Computern) durchaus speichern kann, solche Abweichungen nicht geben kann. Jedenfalls wäre das Durchprobieren aller Möglichkeiten mit vertretbarem Aufwand nicht möglich." - "Mit 'vertretbarem Aufwand' ist gut" schaltet sich Lilly ein. "Ich stelle mir vor, ich würde eine Forschungs-Förderorganisation von der Notwendigkeit zu überzeugen versuchen eine Woche Rechenzeit eines Supercomputing Zentrums zu finanzieren, um die ach so wichtige Kommutativität der Addition natürlicher Zahlen empirisch zu untermauern. Entweder ich würde die Sache nachträglich als Presse-Scherz ausgeben ('Wir wollten mal sehen mit welchem Blödsinn man an Fördermittel kommen kann'), oder ich fände mich in einer Nervenklinik wieder."

"Ich glaube mir ist die rettende Idee gekommen" juble ich dazwischen. "Man muss die Darstellung der Rechnung durch Zeichen noch ernster nehmen als bisher und auch die Argumente x und y in die Darstellung mit einbeziehen. Das geht so: Bisher kennen wir Strichlisten. Jetzt führen wir Strichtexte ein: Sie bestehen aus endlich vielen Zeilen, wobei in jeder dieser Zeilen genau eine nicht-leere Strichliste steht. Der Strichtext ohne Zeile - der leere Strichtext - ist auch dabei. Für ihn schreiben wir "". Also wäre

'////'
'//////'

ein Strichtext, nennen wir ihn t0. Jeder Strichtext t bestimmt eindeutig eine Strichliste t*, die für jede Zeile von t einen Strich hat. Also t0* = '//'. Den Strichtext mit nur einer Zeile nennen wir [x], wenn x die Strichliste dieser Zeile ist. Aus zwei Strichlisten x und y können wir den zweizeiligen Strichtext [x] << [y] bilden und den interessanteren und größeren Strichtext μ(x,y), der für jeden Strich in x eine Zeile hat (und nicht mehr!) und dessen Zeilen alle gleich y sind. Einen solchen Strichtext wollen wir aus naheliegenden Gründen einen Rechtecktext nennen. Zwei Rechtecktexte μ(x,y) und μ(x',y') sind offenbar genau dann gleich, wenn x == x' und y == y' gelten. Für t:=μ(x,y)[definiert als] gilt dann t* == x und t(z) == y für alle z ≤ x.

In Analogie zu unseren Forderungen A3 und A4 können wir auch auf folgendes vertrauen:

A3
Jeden gegebenen nicht leeren Strichtext t kann man dadurch verändern, dass man den letzten Strich in einer seiner Zeilen weglässt und die Zeile tilgt, wenn sie dadurch zur leeren Strichliste geworden ist. Auch nach dieser Veränderung ist t ein Strichtext. Jede derartige Veränderung eines Strichtexts nennen wir eine Kürzung des Strichtexts.
A4
Jeden gegebenen Strichtext t kann man dadurch verändern, dass man einen weiteren gegebene Strichtext u am Ende als neue Zeilen anhängt. Das Ergebnis dieser Veränderung ist wieder ein Strichtext, für den wir t << u schreiben.

(Wir erinnern uns, dass Ruby '<<' für Strichlisten als eine verändernde Operation erklärt. Weil aber Strichtexte und Strichlisten etwas verschiedenes sind können wir die Operation für Strichlisten auch als etaws ganz anderes erklären, hier also, wie eben geschehen als Erzeugung eines neuen Strichtextes mit neuen Zeilen am Ende. Die mit objektorientierten Begriffen bereits infizierte Leserin wird sich fragen, warum ich nicht gleich eine Klasse definiere, die dem Begriff 'Strichtext' eine codierte Form gibt. Damit ein solches Vorgehen für unser Vorhaben, etwa das Kommutativitätsgesetz der Multiplikation zu beweisen, einen Nutzen brächte müssten wir sehr weit ausholen. Wir können ja jetzt schon mit der Klasse N anhand von vielen, bewußt oder zufällig gewählten Zahlen die Gültikeit in konkreten Fällen prüfen. Als Beweis taugt das natürlich nicht, weil ja immer ungeprüfte Zahlen übrigbleiben. Um Programmierfehler zu entdecken ist eine solche Prüfung allerdings eine äußerst wirkungsvolle Methode.)

Jeder Algorithmus Z der Form

Eingabe: Strichtext t
r=''
Solange t nicht leer ist {
  Kürze t in einer durch t alleine eindeutig bestimmten Zeile
  r << '/'
}
Ausgabe: r

heißt Abzählalgorithmus (kurz auch Abzählung). Für die ausgegebene Strichliste schreiben wir Z(t). Weil es für die meisten Strichtexte mehrere fürs Kürzen in Frage kommende Zeilen gibt, muss ein Abzählalgorithmus eine eindeutige Vorschrift zur Wahl der Kürzungszeile festlegen. Was mir nun als Formulierung des Dingcharakters der Striche in den Strichlisten vorschwebt ist die Feststellung:
A5
Sei t ein Strichtext und seien Z1 und Z2 zwei Abzählalgorithmen. Dann gilt Z1(t) == Z2(t).
Man muss sich vor Augen halten: Jeder Abzählalgorithmus entfernt Schritt für Schritt einen Strich aus dem Strichtext und setzt jedesmal einen Strich - was spricht gegen die Vorstellung es sei derselbe der weggenommen wurde ? - ans Ende der Ergebnis-Strichliste an. So ist die Vorstellung stimmig, dass die Ergebnis-Strichliste aus eben den Strichen besteht, aus denen vor seiner Leerung der Strichtext bestand. Diese Vorstellung verlangt zwingend, dass es auf die Reihenfolge des Strichtransfers nicht ankommen kann. Jede Abweichung davon würde darauf hindeuten, dass die Striche sich nicht so benehmen, wie wir es für nebeneinander gesetzte Zeichen selbstverständlich erwarten.

Solche Abweichungen kann es unter den Bedingungen der realen Welt durchaus geben. Stehen unsere Striche etwa als winzige Zeichen auf Mikrofilm und sind wir für das mit dem Kürzen verbundene Auslöschen der Zeichen im Strichtext auf einen nicht ganz sauber geformten Retusche-Pinsel angewiesen, so könnten wir durchaus einmal (ohne es zu bemerken) zwei Striche überpinseln und doch nur einen der Ergebnisliste hinzufügen. Schon hätten wir bei dieser realen Ausführung des Abzählalgorithmus ein anderes Ergebnis, als bei der korrekten Ausführung desselben Algorithmus', oder der korrekten Ausführung eines anderen Abzählalgorithmus' mit demselben Strichtext als Eingabe. Um die Verschiedenheit der Ergebnisse überhaupt feststellen zu können, müssen wir Strichtexte genau wie Strichlisten kopieren und vergleichen können, was bei hinreichend kleinen Dimensionen und schlechtem Handwerkszeug wieder zu Prozess-Schwankungen führen kann. Denken wir uns das ganze sinngemäß wieder in einen Computer übertragen vor, so könnte ein ungewöhnlich heftiger Schauer von kosmischer Strahlung eine Speicherzelle entladen und wieder gäbe es eine Abweichung. In Lilly's vorgestelltem Forschungsvorhaben muss sie also noch Strahlungs-Schutzmaßnahmem im Rechenzentrum vorsehen, damit die Ergebnisse aussagekräftig sind."

"Ich kann mich mit der Feststellung zum Dingcharakter der Striche anfreunden." stellt KS fest. "Ist es jetzt noch ein weiter Weg bis zum Kommutativgesetz der Addition?" - "Nein" kann ich getrost antworten. "Die Kommutativitätsgesetze für Addition und Multiplikation und auch das bisher noch nicht erwähnte Distributivgesetz ebenso wie die beiden Assoziativitätsgesetze folgen aus A5 nun auf nächstliegende Weise." - "Natürlich! Jetzt sehe ich den Clou erst richtig" freut sich KS. "Die Bilder die man im Unterricht an die Wandtafel malt um die arithmetischen Grundgesetze plausibel zu machen übersetzen sich direkt in Zähl-Algorithmen. Das Produkt x*y zweier Strichlisten ergibt erst den oben schon eingeführten rechteckigen Strichtext μ(x,y). Abzählung zeilenweise ergibt x*y, Abzählung spaltenweise ergibt y*x. Beide sind Abzählungen desselben Rechtecks und damit nach A5 gleich." - "Genauso geht es. Ich versuche es aber doch noch ganz genau aufzuschreiben um zu sehen, welche Formalien dazu nötig sind. Immerhin vergleichen wir uns hier mit der Lösung des Problems durch Edmund Landau, und diese Lösung wird allgemein als mustergültig angesehen. Ich definiere drei Zähl-Algorithmen Za, Ze, Zr (hier stehen a,e,r für Anfang, Ende, rechts) indem ich allein die Wahl der Kürzungszeile festlege (mehr Freiheit bleibt ja nicht).

Za: Kürze immer in der ersten Zeile bis der Strichtext leer ist.

Das klingt vielleicht zunächst verwirrend, wird aber klar, wenn man bedenkt, dass nach der Regel für das Kürzen die erste Zeile verschwindet so bald sie geleert ist und damit die nächste Zeile zur ersten wird. Somit ist das 'zeilenweise Abzählen' noch etwas eleganter und zwangsläufiger definiert als ich erwartet hätte.

Ze: Kürze immer in der letzten Zeile bis der Strichtext leer ist.

Auch hier verschwindet die letzte Zeile, wenn sie geleert ist und die bisher vorletzte Zeile wird zur letzten.

Zr: Beginne beim Kürzen bei der ersten Zeile. Nach jedem Kürzen springe in die nächste Zeile, wobei für die letzte Zeile die erste Zeile als nächste angesehen wird. Kürze die durch Sprung erreichte Zeile und fahre fort bis der Strichtext leer ist.

Hier werden also immer die am weitesten rechts stehenden Striche weggelassen. Sind alle Zeilen gleich, so kann man den Strichtext auch aus Spalten aufgebaut denken. Nach einem Lauf über alle Zeilen ist dann die am weitesten rechts stehende Spalte in die Ergebnis-Strichliste transferiert.

Welche Eigenschaften haben nun diese Abzählungen? Wir destillieren einige ganz einfache heraus, die sich als ausreichend herausstellen um die gewünschten allgemeinen Gesetze (Kommutativgesetz für Addition und Multiplikation, und Distributivgesetz) durch einfache Hintereinanderschaltung zu erhalten. Für alle Strichlisten x, y und alle Strichtexte t, u gilt

(1) Za([x]) == x
(2) Za(t << u) == Za(t) << Za(u)
(3) Za([x] << [y]) == Ze([y] << [x])
(4) Za(μ(x,y)) == Zr(μ(y,x))
(5) a(x,y) == x << y
(6) m(x,y) == Za(μ(x,y))
(7) μ(x << y,z) == μ(x,z) << μ(y,z)

Beweis:

(1) Wir betrachten die durch den Algorithmus Za definierte Folge von Zustandsänderungen. Der Anfangszustand ist ([x] ,'') wobei links die abzuzählende Größe steht und rechts der gegenwärtige Stand des Zählergebnisses. Weil der abzuzählende Strichtext einzeilig ist und der Algorithmus Za die Anzahl der Zeilen nicht vergrößern kann ist auch in allen Folgezuständen der abzuzählende Strichtext einzeilig, bis auf den letzten Zustand, wo er leer ist. Bei jedem Stand der Abzählung haben wir also eine Zahlenpaar (z,r). Nach dem nächsten Schritt haben wir das Zahlenpaar (z.chop!, r << '/') . Offensichtlich ist z << r == z.chop! << (r << '/') . Wird doch auf der rechten Seite der behaupteten letzten Gleichung von z einmal ein Strich weggelassen und anschließend an r angefügt. Damit ist die Größe z << r unabhängig vom Stand der Zählung. Weil im Endzustand z == '' gilt, ist r == x .

(2) Bei Ausführung von Za(t << u) werden erst die Zeile von t abgearbeitet. Ist man mit ihnen fertig hat man das Ergebnis Za(t) an das dann bei Fortsetzung der Abzählung die Striche angehängt werden, die auch bei der Ausführung von Za(u) entstünden. Das führt zu Endergebnis Za(t) << Za(u) .

(3) Weil nur zwei Zeilen im Spiel sind, führen beide Seiten der behaupteten Gleichung, als Algorithmen, zur selben Abfolge von Aktionen.

(4) Wenn Zr eimal über alle Zeilen des rechteckigen Strichtextes μ(y,x) läuft, wächst das Zählergebnis um y. Wenn Za eimal über eine Zeilen des rechteckigen Strichtextes μ(x,y) läuft, wächst das Zählergebnis ebenfalls um y. Beide diese Aktionen kommen bei gleichzeitiger Ausführung gleichzeitig zum Ende.

(5) Das ist die Definition der Addition a.

(6) Das ist die Definition der Multiplikation m.

(7) Weil alle Zeilen von μ(x,z) << μ(y,z) gleich sind, liegt ein Rechtecktext μ(w,z) vor. Dann gilt w = x << y und damit die Behauptung.

Das Kommutativgesetz der Addition ergibt sich durch eine Kette von Gleichsetzungen. Die begründende Eigenschaft für die Gleichheit ist durch ihre Nummer in obiger Liste angegeben.

a(x,y) ==(5) x << y
x << y ==(1) Za([x]) << Za([y])
Za([x]) << Za([y])==(2) Za([x] << [y])
Za([x] << [y]) ==(3) Ze([y] << [x])
Ze([y] << [x]) == A5 Za([y] << [x])
Za([y] << [x]) ==(2) Za([y]) << Za([x])
Za([y]) << Za([x]) ==(5) a(y,x)

Also a(x,y) == a(y,x), oder in üblicher Schreibweise x + y = y + x.

Kommutativgesetz der Multiplikation:

m(x,y) ==(6) Za(μ(x,y))
Za(μ(x,y)) == A5 Zr(μ(x,y))
Zr(μ(x,y)) ==(4) Za(μ(y,x))
Za(μ(y,x)) ==(6) m(y,x)

Also m(x,y) == m(y,x), oder in üblicher Schreibweise x*y = y*x.

Distributivgesetz:

m(a(x,y),z) ==(5,6) Za(μ(x << y,z)
Za(μ(x << y,z) ==(7) Za(μ(x,z) << μ(y,z))
Za(μ(x,z) << μ(y,z)) ==(2) Za(μ(x,z)) << Za(μ(y,z))
Za(μ(x,z)) << Za(μ(y,z))==(6) m(x,z) << m(y,z)
m(x,z) << m(y,z) ==(5) a(m(x,z),m(y,z))

Also m(a(x,y),z) == a(m(x,z),m(y,z)), oder in üblicher Schreibweise (x+y)*z = x*z + y*z.

Das Assoziativitätsgesetz der Addition (x + y) + z = x + (y + z) ergibt sich aus den Abzählungen Za und Ze des dreizeiligen Strichtexts [x] << [y] << [z]. Als Zwischenergebnis von Za nach dem Verschwinden der Zeilen [x] und [y] erhält man x + y und als Endergebnis (x + y) + z. Als Zwischenergebnis von Ze nach dem Verschwinden der Zeilen [z] und [y] erhält man z + y und als Endergebnis (z + y) + x. Nach dem schon bewiesenen Kommutativität der Addition gilt (z + y) + x = x + (z + y) = x + (y + z) womit das Assoziativitätsgesetz der Addition in der oben angegebenen Form bewiesen ist.

Das Assoziativitätsgesetz der Multiplikation lautet (x*y)*z = x*(y*z). Um ohne umständliche Operationen zu einem Beweis zu kommen, müssen wir den Schritt, der uns von Strichlisten zu Strichtexten geführt hat 'auf höherer Ebene' widerholen: Wir müssen 'Strichbücher' betrachten, deren Seiten Strichtexte enthalten - je Seite einen Text, die Rückseite also leer - so dass das 'Buch' nur eine Metapher für eine Reihe endlich vieler Strichtexte ist. Im selben Sinn war der Strichtext nur eine Metapher für eine endliche Reihe von Strichlisten und die Strichliste eine Metapher für eine endliche Reihe von Strichen. Genauso, wie der Übergang von Strichlisten zu Strichtexten sich durch Einführen eines neuen Zeichens 'n' (neue Zeile) beschreiben lies, brauchen wir jetzt nur das neue Zeichen 'p' (neue Seite). In diesem Sinn lässt sich die Konstruktion noch einige Schritte weiterführen ohne dass uns die Begriffe ausgehen: neues Buch, neue Reihe im Bücherregal, neues Bücherregal, neue Regalreihe im Lesesaal, neuer Lesesaal, ... .

Nun aber zu unseren drei Zahlen x, y, z. Wir betrachten ein Strichbuch b mit z Seiten wobei jede Seite den Strichtext μ(x,y) trägt. Den Algorithmus Za erweitern wir auf Bücher dadurch dass wir 'Seite für Seite' zählen. Wenn also der Text einer Seite gelöscht ist, wir beim ersten Zeichen der ersten Zeile der nächsten Seite mit dem Löschen fortfahren. Natürlich ist der Algorithmus am Ende angekommen, wenn kein Strich zum Löschen mehr da ist. Weil Za(μ(x,y)) = x*y gilt, ist für den erweiterten Algorithmus Za dann Za(b) = (x*y)*z. Nun erfinden wir eine neue Art Bücher zu lesen: Umlättern nach jeder gelesenen Zeile. Natürlich bedeutet "Umblättern" der letzten Seite den Rücksprung zum Beginn der ersten Seite. Weil alle Zeilen y Striche enthalten liefert jeder 'Durchgang' y*z Striche. Weil jede Seite x Zeilen hat sind wir fertig mit lesen nach (y*z)*x. Weil also (x*y)*z und (y*z)*x die Zählergebnisse zweier Zählalgorithmen desselben Strichbuchs sind, gilt (x*y)*z = (y*z)*x = x*(y*z) wobei das letzte Gleichheitszeichen wegen der schon bewiesenen Kommutativität der Multiplikation gerechtfertigt ist".

"Irgendwie finde ich 'Büchermethode' recht gewöhnungsbedürftig" wirft Lilly ein. "Ein etwas 'mathematischeres' Verfahren wäre mir lieber. Ich erinnere mich daran wie in der Anfängervorlesung an der Universität argumentiert wurde. Ich glaube ich bring das noch zusammen: Wir wollen x*(y*z) = (x*y)*z für alle natürlichen Zahlen beweisen. Das Zauberwort ist 'Induktion' oder Schluß von n auf n+1. Wenn ihr nichts dagegen habt, nenne ich das 'z' um in 'n' das hilft meiner Intuition und meiner Erinnerung. Wir wollen also zeigen x*(y*n) = (x*y)*n für alle natürlichen Zahlen x,y,n. Wenn das allgemeingültig sein soll, so muss es erst recht für n=0 (='') und für n=1(='/') gelten. Hier haben wir erleichternde Umstände weil wir wissen (und hoffentlich auch aus unserer Definition der Multiplikation von Strichlisten folgern können): Für jede natürliche Zahl a gilt a*0=0 und a*1=a. Namit rechnen wir mit n=1 (n=0 geht auch, nur noch leichter) x*(y*1) = x*y, (x*y)*1=x*y. Jetzt nehmen wir an wir hätten x*(y*n)=(x*y)*n für irgendeine natürliche Zahl n bewiesen, dann sagt meine Erinnerng dass eine einfache Rechnung (ein 'Selbstläufer') zeigt, dass unsere Gleichung auch für n+1 anstelle von n gilt. Die Devise lautet 'Man gucke eben nach' (das hat einer meiner Klassenkameraden in einem germanistischen Seminar geäußert und hat damit seinen bisher gepflegten Ruf als gebildeter junger Mann (obgleich 'vom Land') gründlich ruiniert. Also x*(y*(n+1)) = x*(y*n) + x*y (da brauchen wir das Distributionsgesetz, das wir erfreulicherweise schon bewiesen haben) = (x*y)*n (nach Annahme) + x*y = (x*y)*(n+1) und damit was wir zeigen wollten. Denn, haben wir einen beliebigen Wert von n so 'klettern' wir vom bewiesenen Fall n=1 zu n=2 (legitim), zu n=3 in endlich vielen Schritten zum gefragten Wert von n.". "Bravo!!!, kann ich meine Anerkennung für diese Ableitung aus dem Stegreif nicht verhehlen. Jetzt erinnere ich mich an eine Szene aus einer Vorlesung von Professor Seebach in München. Obwohl die 'vollständige Induktion in der Vorlesung nur gestreift worden war (es ging um Gruppentheorie) berichtete er nach der Vorlesung noch einem kleinen Kreis Interessierter von einer Tagung zur Pädagogik der Mathematik von der er gerade zurückgekommen war und die sich ausführlich mit der Rolle der Induktion in der Mathematik auseinandersetzte. Es sei von nicht nur einem der Vortragenden immer wieder betont worden, dass das, was Lilly uns hier aus der Erinnerung präsentiert hat ganz irreführend sei und im ernsthaften Unterricht durch eine moderne Variante zu ersetzen sei. Der Schritt von der Induktionsvoraussetzung (unserem Fall n=1) über eine Induktionsanahme (unser Fall n beliebig aber fest) zu dem Ergebnis (n beliebig) sei ein einziger logischer Großschritt und kein mühseliges Hangeln von Zahl zu Zahl. Um die transfiniten Welten Cantors zu erreichen oder gar zu durchmessen sei diese Hangelvorstellung gänzlich ungenügend und man solle besser die lernende Jugend damit verschonen." "Wir haben uns bisher vor den Mühen des Hangels nicht gescheut - war das Abzählen der Striche in vielzeiligen Strichtexten doch auch ein Hangeln. Aber, wenn man sich geigneter Begriffe bedient nichts mühseeliges sondern in Gedanken eigentlich doch in einem logischen Großschritt mit gesichertem Ergebnis durchführbar.

Last modification: 2022-11-07