INHALT

Teil II
Zwei echte Programme – und warum Programmieren schwierig ist!

Zwei echte Programme

Was bedeutet es nun, dass ein Programm aus Sätzen besteht, also aus hintereinander ausgeführten Schritten, um damit eine Funktion auszurechnen? Nochmals zur Erinnerung: Wir unterscheiden zwischen dem, was eine Funktion beschreibt, und wie das berechnet wird: Einem Menschen ist es intuitiv klar, wie die Buchstabenfolgen f(x,y)=x+y und g(x)=x2 zu verstehen sind und was sie bedeuten, nämlich „Summe von x und y“ und „x zum Quadrat“. Einer Maschine aber müssen wir sagen, wie sie das ausrechnen kann. Eine Möglichkeit für die Berechnung der Quadratfunktion ist x*x. Zusätzlich müssen wir der Maschine dann auch noch sagen, wie die Multiplikation funktioniert, die ihrerseits wiederum aus einzelnen Schritten besteht.

Sehen wir uns deshalb kurz an, wirklich nur ganz kurz, wie ein Programm jeweils aussieht, das unsere Quadrat- und Summenfunktion berechnet. Lassen Sie uns mit der Summenfunktion beginnen. Eingaben sind die zwei Summanden x und y, Ausgabe soll deren Summe sein. Lassen Sie uns für den Moment annehmen, dass wir eine einfache Programmiersprache benutzen, die zwar die Addition beliebiger Zahlen nicht beherrscht, aber die einfachere Addition von 1 und Subtraktion von 1. Wir werden jetzt versuchen, mit diesen beiden einfachen Funktionen beliebige Summen zu berechnen.

Fragen Sie mich nicht, wie man darauf kommt, aber x+y ist ja das Gleiche wie 1+x+(y-1). Wir haben also links eine Eins addiert und rechts eine Eins abgezogen, was sich gegenseitig aufhebt. 1+x+(y-1) wiederum ist das Gleiche wie 1+1+x+(y-1-1). Und so weiter. Beispielsweise ist 3+2 das Gleiche wie (1+3)+(2-1), also 4+1, und das ist das Gleiche wie (1+4)+(1-1), also 5+0. Das ist unser Endergebnis: 5.

Wir können also beliebige Summen durch Addition und Subtraktion von 1 berechnen, indem wir vom zweiten Summanden (das ist ja y) eine Eins abziehen und gleichzeitig eine Eins zum ersten Summanden, also x, hinzufügen. Das machen wir genau y-mal: Wir ziehen also y-mal eine Eins von y ab, bis das Ergebnis null ist, und addieren y-mal eine Eins zu x.

Die ursprünglichen Werte von x und y liegen im oben eingeführten Speicher, und in unserem Programm können wir die entsprechenden Werte lesen und überschreiben (dazu sagen Informatikerinnen und Informatiker speichern und noch genauer in x speichern oder in y speichern). Unser Programm sieht dann wie folgt aus.

Programm „Summe“.
Eingabe: zwei Zahlen x und y
Ausgabe: Summe x plus y
Wiederhole, solange y ungleich 0 ist:

  1. Addiere 1 zu x und speichere das Ergebnis 1+x in x
  2. Subtrahiere 1 von y und speichere das Ergebnis y-1 in y

Gib x aus

Als echter Code sieht das so aus: while (Bedingung) ist dabei die Wiederholungsanweisung, die so lange ausgeführt wird, wie die Bedingung in der Klammer wahr ist. ≠ ist die Ungleichheit. Mit dem Linkspfeil ← beschreiben wir den Vorgang des Speicherns: x ← x+1 sagt, dass der Wert von x+1 an die für x vorgesehene Stelle im Speicher geschrieben wird. output gibt das Ergebnis auf dem Bildschirm aus. Die geschweiften Klammern dienen nur der Gruppierung.

summe(x, y) {

while (y≠0) {

x ← x+1;
y ← y-1;

}
output x;

}

Ein Computer arbeitet das Programm nun wie folgt ab. Nehmen wir den Fall der Eingaben x=3 und y=2.

  1. Weil y≠0 ist, wird im ersten Schritt x zu 3+1=4 und y zu 2-1=1.
  2. y ist immer noch ungleich null, also wird x jetzt zu 4+1=5 und y zu 1-1=0.
  3. Jetzt gilt y≠0 nicht mehr, die Wiederholung ist beendet.
  4. Das Ergebnis ist der letzte Wert von x, nämlich 5.

Das ist vielleicht etwas ungewohnt, aber doch eigentlich ganz logisch. Behalten Sie bitte im Kopf, dass wir hier das schwierigere Problem „berechne beliebige Summe” mithilfe zweier einfacherer Operationen gelöst haben, nämlich der Addition und Subtraktion von 1. Das ist eine Kernidee der Programmierung: Wir lösen ein schwieriges Problem mit einfachen Schritten.

Lassen Sie uns jetzt das gleiche Spiel für die Quadratfunktion spielen. Diesmal nehmen wir an, dass unsere Programmiersprache wie eben die Subtraktion von 1 zur Verfügung steht und zusätzlich die Addition von Zahlen. Letztere haben wir ja eben selbst programmiert, sodass wir dieses Programm einfach verwenden können.

x mit x zu multiplizieren ist das Gleiche, wie x-mal die Zahl x zu addieren. Denn 3*3 ist 3+3+3 und 4*4 ist 4+4+4+4. Um die Multiplikation mit der allgemeinen Addition und der Subtraktion von 1 zu beschreiben, verwenden wir wieder so einen Trick: x*x ist das Gleiche wie x+(x-1)*x. Und das ist das Gleiche wie x+x+(x-1-1)*x. Aha! Das wiederum ist das Gleiche wie x+x+x+(x-1-1-1)*x. Und so weiter bis der Wert in der Klammer null ist. So haben wir jetzt die Quadratfunktion mithilfe der Subtraktion von 1 und der allgemeinen Addition definiert.

In diesem Fall müssen wir uns ein Zwischenergebnis merken, das wir jeweils als z abspeichern. Am Anfang setzen wir z auf den Wert 0. Betrachten wir jetzt das Beispiel 3²:

  1. Das ist das Gleiche wie 3*3, was das Gleiche ist wie 3+(3-1)*3. Der linke Teil der Summe ist unser Zwischenergebnis.
  2. Wir addieren also diese linke 3 zu z, das ja ursprünglich 0 ist, und speichern das Ergebnis 3+0 in z.
  3. Dann machen wir weiter: z+2*3 ist das Gleiche wie z+3+(2-1)*3, also z+3+1*3.
  4. Wir addieren die linke 3 zum Zwischenergebnis z und schreiben den Wert z=3+3=6 in den Speicher für z.
  5. z+1*3 ist dann das Gleiche wie z+3+(1-1)*3, was das Gleiche ist wie z+3+0*3.
  6. Wir addieren die linke 3 zum Zwischenergebnis z, das nun den Wert z=6+3=9 hat.
  7. 0*3 ist 0, also können wir aufhören.
  8. Das Zwischenergebnis z hat sich in jedem Schritt geändert, und dessen letzter Wert ist 9. Stimmt!

Weil wir in jedem Schritt den Wert von x zum Zwischenergebnis z addieren müssen, können wir x nicht wie im Fall der Summe in jedem Schritt ändern. Stattdessen benötigen wir ein zweites Zwischenergebnis v, das hintereinander den Wert von x, x-1, x-2, …, 0 speichert.

Programm „Quadrat“.
Eingabe: eine Zahl x
Ausgabe: x zum Quadrat, also x*x
Speichere 0 im Zwischenergebnis z
Speichere x im Zwischenergebnis v
Wiederhole, solange v ungleich 0 ist:

  1. Addiere x zu z und speichere das Ergebnis z+x in z
  2. Subtrahiere 1 von v und speichere das Ergebnis v-1 in v

Gib z aus

Als Code:
quadrat(x){

z ← 0;
v ← x;
while (v≠0) {

z ← z+x;
v ← v-1;

}
output z;

}

Simulieren wir für die Eingabe x=3 den Computer, der das Programm ausführt:

  1. Zuerst wird z auf 0 gesetzt und v auf den Wert von x, also 3.
  2. Weil 3≠0 ist, wird z auf 0+3=3 gesetzt und v auf 3-1=2.
  3. Weil 2≠0 ist, wiederholen wir das: z wird zu 3+3=6 und v zu 2-1=1.
  4. Wiederum ist 1≠0, also wiederholen wir die Schritte erneut: z wird zu 6+3=9 und v zu 1-1=0.
  5. Jetzt ist die Wiederholung beendet, denn 0≠0 gilt nicht, und es wird z=9 ausgegeben.

So einfach funktionieren Programme. Auf dieselbe Art und Weise werden E-Mail-Programme, Suchmaschinen, Steuergeräte in Autos und in Bäckereien programmiert. Wirklich!

Falls Sie sich wundern sollten: Wir haben in unsere Programme bewusst einen kleinen Fehler eingebaut, den wir hinterher finden werden.

Bibliotheken und Wiederverwendung

Wenn unsere Programmiersprache das Additionssymbol + nicht kennt, können wir es einfach durch unser Summenprogramm oben ersetzen (Änderung in Blau) und erhalten ein neues Programm quadrat2:

quadrat2(x) {

z ← 0;
v ← x;
while (v≠0) {

z ← summe(z,x);

v ← v-1;

}
output z;

}

Die Verwendung von vorher erstellten Programmen in einem neuen Programm ist ein ungeheuer mächtiges Prinzip, das man erst in den Sechzigerjahren entdeckt hat. Es ist deswegen so mächtig, weil es uns einerseits erlaubt, Probleme in kleinere Probleme zu zerlegen, unabhängig voneinander zu lösen und dann die Lösungen zusammenzusetzen. Es hilft uns also unter anderem bei der Organisation unserer Programmierarbeit.

Andererseits erlaubt dieses Prinzip es auch, einmal erstellte Funktionalität anderen Programmiererinnen und Programmierern zur Verfügung zu stellen. Informatiker nennen solche Programmpakete, die anderen zur Nutzung in deren eigenen Programmen zur Verfügung gestellt werden, Bibliotheken, die wir oben mit Backbüchern verglichen haben. Vielleicht haben Sie schon einmal von der Programmiersprache Java gehört, oder von einer Sprache namens Python? Diese Sprachen werden unter anderem deswegen von sehr vielen Programmierern auf der ganzen Welt genutzt, weil sie riesige Bibliotheken für alle möglichen Funktionalitäten zur Verfügung stellen, unter anderem um Daten ins Internet zu schicken, Maschinenlernen zu verwenden oder natürlichsprachigen Text zu analysieren. Programmierer müssen diese Funktionalitäten dann nicht selbst erstellen, sondern können direkt von der Arbeit anderer profitieren, indem sie die entsprechenden Programme wiederverwenden.

Wenn Sie genau aufgepasst haben, werden Sie bemerken, dass wir zwei Konzepte für dieselbe Idee eingeführt haben. Oben hieß es, dass sich Programmiersprachen darin unterscheiden, wie sie „bestimmte Sachverhalte“ oder „technische Schritte“ ausdrücken, und dass manche Sprachen eher geeignet sind, über „Fenster“, „Knopf“ und „Mauszeiger“ zu sprechen, und andere über „Entfernung zum voranfahrenden Auto“. Hier sagen wir jetzt, dass „bestimmte Sachverhalte“ in Programmen erfasst werden, also für Probleme Lösungen programmiert werden, die dann in Bibliotheken zur Wiederverwendung zur Verfügung stehen. Ihre Beobachtung ist richtig. Das ist in der Tat zweimal dieselbe Idee in unterschiedlichen Ausprägungen: Wiederkehrende Konstruktionen sollen möglichst einfach verwendet werden können. Das zugrundeliegende Prinzip nennen Informatiker Abstraktion.

Fast geschafft: Prozessor und Maschinensprache

Vielleicht haben Sie beim Lesen den Verdacht entwickelt, dass wir irgendwie getrickst haben. Denn wir haben unsere Funktionen unter Zugrundelegung anderer, einfacherer Funktionen programmiert, was das Problem ja streng genommen nur verschiebt. Zur Berechnung einer beliebigen Summe haben wir die einfacheren Operationen „plus 1” und „minus 1” ganz nonchalant als gegeben vorausgesetzt. Im Quadratprogramm konnten wir uns gerade noch retten, weil wir die Addition vorher selbst programmiert hatten. Die „minus 1”-Operation haben wir dort aber immer noch benötigt.

Wenn Sie sich erinnern, war die Situation beim Kuchenbacken ganz ähnlich. In Rezepten werden viele Einzelschritte stark vereinfacht dargestellt, wenn etwa stillschweigend angenommen wird, dass „Backen“ aus den folgenden Vorgängen besteht: „Ofentür öffnen“, „alle Bleche aus dem Ofen nehmen“, „Ofentür schließen“, „Ofen vorheizen“, „Ofentür öffnen“, „Kuchenform hereinschieben“, „Backen“, „Ofen ausschalten“, „Ofentür öffnen“, „Kuchen herausnehmen“, „Ofentür schließen“. Dort wird vorausgesetzt, dass der Bäcker oder die Bäckerin bei komplexen Schritten weiß, aus welchen Einzelschritten sie zusammengesetzt sind.

Programme werden also unter Zuhilfenahme einfacher Operationen geschrieben. Und dann schreiben wir noch größere Programme unter Zuhilfenahme dieser einfachen Operationen, anderer Programme und der eben eingeführten Bibliotheken. Darauf aufbauend schreiben wir noch größere Programme und so weiter. Wir können Programme in diesem Sinn aufeinanderstapeln.

Ich habe aber noch nicht erklärt, wie die Ergebnisse der einfachen zugrundeliegenden Operationen ausgerechnet werden. Und vielleicht haben Sie nicht vergessen, dass wir noch gar nicht ganz genau gesagt haben, was ein Prozessor nun tut. Das können wir jetzt verstehen: Der Prozessor stellt genau diese einfachen zugrundeliegenden Operationen zur Verfügung! Er kann auf der Hardware sehr einfache Operationen berechnen wie – Sie ahnen es – die Addition oder Subtraktion von eins und die Multiplikation mit zwei oder die Division durch zwei. Er kann überprüfen, ob ein Wert null ist, kann beliebige Stellen in einem Programm als Nächsten auszuführenden Schritt auswählen und so weiter. Für diese sehr einfachen, auf der Hardware ausgeführten Operationen gibt es wieder eine eigene Programmiersprache, die man Maschinensprache nennt. Die Maschinensprache für Prozessoren von Intel umfasst ca. 1000 mögliche einfache Operationen.

Programme in anderen Programmiersprachen wie Java oder Python werden letztlich in Programme in Maschinensprache übersetzt. Das ist tatsächlich im Sinn einer Übersetzung von Deutsch nach Englisch zu verstehen.

Vielleicht haben Sie das Wort Compiler schon einmal gehört: Compiler erledigen genau diese Übersetzung in Maschinensprache. Die Idee ist ganz ähnlich zur Verwendung des summe-Programms im quadrat2-Programm in der Zeile z ← summe(z,x). Vielleicht erinnern Sie sich an die Bemerkung zur prinzipiellen Ähnlichkeit von Bibliotheken und unterschiedlichen Programmiersprachen. Die Verwendung des Worts summe steht ja für die Codezeilen, die das summe-Programm implementieren. In Wahrheit funktioniert es ein kleines bisschen anders, aber man kann sich das so vorstellen, dass das Wort summe im quadrat2-Programm in genau die Codezeilen übersetzt wird, die dem summe-Programm entsprechen. Und ganz genauso stehen die Wiederholungsanweisung while, die Zuweisung ←, Addition und Subtraktion von 1 sowie die Ausgabefunktion output für kleine eigenständige Programme in Maschinensprache, in die sie tatsächlich auch übersetzt werden.

Verblüffenderweise lassen sich E-Mail-Programme, Bäckereisteuerungen und alle anderen Funktionen in diese einfache Maschinensprache übersetzen. Und ein Prozessor kann dann Programme in Maschinensprache direkt in der Hardware ausführen. Das heißt, der Prozessor erhält Eingabesignale in Form von „Strom“ und „kein Strom“, die beispielsweise die Addition von 1 zu einer Zahl 7 repräsentieren, und er liefert Ausgabesignale ebenfalls in Form von „Strom“ und „kein Strom“, die dann als das Ergebnis der Addition interpretiert werden. Das sind die berühmten Einsen und Nullen, die wir alle mit Computern verbinden.

Dass man mit Einsen und Nullen beliebige Zahlen darstellen kann, ist seit dem 17. Jahrhundert bekannt. Uns führt das hier zu weit, aber die Idee können wir kurz verstehen. Stellen Sie sich das so vor: Wir Menschen benutzen zur Darstellung von Zahlen zehn Ziffern, nämlich 0, 1, 2, …, 9. Wenn wir die Zehn darstellen wollen, benötigen wir mehr als eine Ziffer: 1 und 0, also 10. Elf ist 1 und 1, also 11. Neunundneunzig ist 9 und 9, also 99. Wenn wir die Hundert darstellen wollen, benötigen wir drei Ziffern: 1 und 0 und 0, also 100. Und so weiter. Wenn wir nur Nullen und Einsen zur Verfügung haben, können wir dasselbe Prinzip verwenden: So wie wir Menschen eine Zehn nicht mit nur einer Ziffer darstellen können, können Computer eine Zwei nicht mit nur einer Ziffer darstellen. Also wird eine zweite Ziffer hinzugefügt, und 2 wird dargestellt als 1 und 0, also 10. 3 stellen wir als 1 und 1 dar, also 11. Für die Vier benötigen wir eine weitere Ziffer, so wie wir für die Hundert eine weitere benötigt haben: Vier wird als 1 und 0 und 0 geschrieben, also 100. Und so weiter. Die Zehn wird übrigens als 1010 dargestellt, die Hundert als 1100100.

Weil die Programmschritte, die man mit Maschinensprache beschreiben kann, sehr feingranular sind, werden die entsprechenden Programme sehr schnell sehr groß und allein deswegen schwierig zu verstehen und zu warten. In der Frühzeit der Computer wurden Programme tatsächlich direkt in Maschinensprache verfasst. Heute macht das eigentlich niemand mehr, sondern benutzt die für den Menschen besser verständlichen Programmiersprachen, die mit den angesprochenen Übersetzern für Programme in Maschinensprache übersetzt werden.

Drei Bemerkungen: Zustand; drei Anweisungen reichen aus; Vermenschlichung

Erlauben Sie mir noch drei Bemerkungen zum Schluss dieses Teils. Ich finde sie wichtig, aber wenn beim ersten Lesen dieses Abschnitts nicht alles klar wird, werden Sie den Rest trotzdem verstehen können.

  1. Die Menge aller Zwischenergebnisse zu einem bestimmten Zeitpunkt nennt man den Zustand eines Programms. Der Zustand wird während der Programmausführung im Speicher abgelegt. Jeder Schritt der Programmausführung verändert diesen Zustand, weil Zwischenergebnisse und in unserem Beispiel auch die Werte der Eingaben verändert werden. Oft gibt es sehr viele solcher möglichen Zustände. Deren große Anzahl macht es schwierig, Programme zu schreiben und zu verstehen. Es ist also nicht nur die Anzahl der Schritte, die ein Programm komplex werden lässt (die Software in einem Auto besteht heute aus Hunderten von Millionen Zeilen Code), sondern eben auch die Anzahl der möglichen Zustände.
  2. Oben haben wir erläutert, dass Programmiersprachen sich unter anderem darin unterscheiden, wie sie bestimmte Sachverhalte in einem bestimmten Kontext besonders kurz und knapp ausdrücken können: Wir haben über „Fenster“ und „Knöpfe“ gesprochen und angedeutet, dass die Ansteuerung von Maschinen mit speziellen Programmiersprachen erfolgt, die das besonders gut ausdrücken können. In unseren Programmen oben haben wir drei solcher Sachverhalte einfach so verwendet, ohne das näher zu erklären, weil sie so natürlich sind: (1) das Ablegen von Werten oder Zwischenergebnissen im Speicher, das man deswegen auch speichern nennt – das sind die Zeilen mit dem Linkspfeil; (2) die Ausführung einer Zeile nach der nächsten – das wird durch das Semikolon am Ende signalisiert; und (3) die Wiederholung – die while-Anweisung, die wir schon erklärt haben. Ich finde das nach wie vor wirklich sehr verblüffend: Seit den 1960ern ist bekannt, dass alle Funktionen, die man überhaupt berechnen kann, sich mit diesen drei Sachverhalten zuzüglich Addition und Subtraktion programmieren lassen. Alle!Warum dann der eigenartige Einschub zu Funktionen, die man überhaupt berechnen kann? Verrückterweise gibt es auch Funktionen, die man nicht berechnen kann, was kaum vorstellbar ist! Ein berühmtes Beispiel ist eine Funktion F, die als Eingabe ein Programm P erhält und für jedes „Eingabeprogramm“ P ausrechnen soll, ob dieses Programm P für jede Eingabe E irgendwann mit seiner Berechnung fertig wird oder ob es passieren kann, dass das Programm unendlich lange abläuft. Wir werden gleich sehen, dass das für unsere Beispiele der Quadrat- und Summenprogramme oben im Fall der Eingabe negativer Zahlen der Fall ist. Eine solche Funktion F, die für alle Programme P und alle Eingaben E ausrechnet, ob die Berechnung irgendwann aufhört, sieht also so aus: F(P)= „ja“, falls P(E) für alle Eingaben E irgendwann fertig wird, und andernfalls „nein“. Für eine solche Funktion gibt es kein Programm! Man weiß das seit den 1930ern, aber darum wollen wir uns hier nicht weiter kümmern.
  3. Vielleicht ist Ihnen aufgefallen, dass wir im gesamten Text ganz intuitiv für Maschinen Tätigkeitswörter verwendet haben, die man eigentlich eher bei Menschen oder allgemein Lebewesen sehen würde: „auf einen Reiz reagieren“, „interagieren“, „kommunizieren“, „erkennen“, „berechnen“, „lernen“ und so weiter. Philosophen haben viel darüber nachgedacht. Brisant wird das, wenn man annimmt oder sprachlich suggeriert, dass Maschinen über Sachverhalte „entscheiden“ und gegebenenfalls sogar „verantwortlich“ oder „haftbar“ sein können. Mit Ausnahme von Verantwortlichkeit und Haftung finde ich diese sprachliche Vermenschlichung eingängig und völlig in Ordnung. Verheimlichen will ich aber nicht, dass es Denkschulen im Zusammenhang mit dem sogenannten Transhumanismus gibt, die Maschinen irgendwann echte Intelligenz oder sogar Emotionen oder Verantwortung zuerkennen. Ich für meinen Teil halte das, kurz gesagt, für abwegig.

Präzision beim Programmieren; Fehler

Vielleicht haben auch Sie schon einmal ein Kuchenrezept ausprobiert, das Sie nicht ganz verstanden oder missverstanden haben, und sich dann bei genauerer Betrachtung des Ergebnisses dazu entschieden, den Kuchen vielleicht doch lieber wegzuwerfen als ihn zu essen. Manchmal liegt das an einem selbst. Manchmal liegt es aber auch daran, dass Rezepte nicht präzise genug beschrieben sind oder dass sie schlicht Fehler enthalten.

Vielleicht ist Ihnen das aufgefallen: Wenn die Eingabe y für unsere Summenfunktion oder die Eingabe x für unsere Quadratfunktion oben eine negative Zahl ist, wird das Programm niemals fertig. Denn eine negative Zahl minus eins kann niemals null ergeben: Während der Ausführung des Programms subtrahieren wir unendlich oft minus eins. Das Problem ist, dass wir vergessen haben, diesen Spezialfall zu berücksichtigen. Wir waren nicht hinreichend präzise. (Der Fehler lässt sich in beiden Programmen dadurch beheben, dass wir das ≠ in der while-Bedingung durch das Größer-Zeichen > ersetzen. Überzeugen Sie sich selbst. Und wenn Sie danach genau hinsehen, haben wir noch etwas vergessen: Wir haben im Programm nicht verlangt, dass die Eingaben ganze Zahlen sein müssen. Wenn man als Eingabe 4,2 eingibt, wird das Programm ebenfalls niemals fertig. Das Ersetzen von ≠ durch > behebt aber auch dieses Problem.)

Mangelnde Präzision in dieser und anderen Ausprägungen ist ein großes Problem beim Programmieren, und unter anderem deswegen ist Programmieren nicht immer ganz so einfach. In einem sehr lustigen Videoclip (Exact Instructions Challenge: This is why my children hate me) wird das Problem an einem Beispiel sehr schön klar: Ein Vater bittet seine kleinen Kinder, genau aufzuschreiben, welche Schritte er durchführen soll, um ihnen ein Erdnussbutter-Marmeladen-Sandwich zu schmieren. Probieren Sie das ruhig einmal mit Ihren Kindern aus! Es stellt sich schnell heraus, dass das schwieriger ist als gedacht. Beispielsweise missversteht der Vater, natürlich absichtlich, die Anweisung „Erdnussbutter auf den Toast streichen“ so, dass er die Erdnussbutter auf eine Kante des Brots streicht anstatt auf die Schnittfläche. Oder er benutzt seine Hände, um Marmelade aus dem Glas zu holen, weil die Kinder vergessen haben, dazu den Einsatz eines Löffels vorzuschlagen. Oder er scheitert schon zu Beginn, weil es keine Anweisung gab, die Kühlschranktür vor Entnahme des Marmeladenglases zu öffnen.