Das Ziel dieses Kurses ist es, dir beizubringen, wie ein Informatiker zu denken. Die Art wie Informatiker denken vereinigt die besten Züge der Mathematik, der Ingenieur-Wissenschaften und der Naturwissenschaften. Wie Mathematiker benutzen Informatiker formale Sprachen um Ideen - insbesondere Berechnungen die von Computern ausgeführt werden sollen - aufzuschreiben. Wie Ingenieure entwerfen Informatiker Dinge, indem sie Komponenten zu Systemen zusammensetzen und Vor- und Nachteile von Alternativen überdenken. Wie Wissenschaftler beobachten sie das Verhalten komplexer Systeme, formulieren sie Hypothesen und testen Voraussagen.
Die wichtigste einzelne Fähigkeit für einen Informatiker ist Problemlösen. Damit ist die Fähigkeit gemeint, Probleme zu formulieren, kreativ über Lösungen nachzudenken und eine Lösung klar und genau zu beschreiben. Programmieren lernen ist eine hervorragende Gelegenheit um Problemlösungs--Fertigkeiten zu entwickeln und zu üben. Deswegen heißt dieses Kapitel "Über das Programmieren"
Auf der einen Ebene wirst du lernen zu programmieren, was an sich eine nützliche ertigkeit ist. Auf einer anderen Ebene wirst du Programmieren als Mittel zum Erreichen bestimmter Zwecke einsetzen. Im Verlauf des Lernens werden dir das Wie, Warum und Wozu klarer werden.
Die Programmiersprache, die du lernen wirst, ist Python. Python ist ein Beispiel für eine höhere Programmiersprache; höhere Programmiersprachen werden oft auch problemorientierte Sprachen genannt. Andere höhere Programmiersprachen, von denen du vielleicht schon gehört hast sind C, C++, Perl und Java.
Wie du vielleicht schon aus dem Ausdruck "höhere Progammiersprache" schließt, gibt es auch Sprachen auf "niedrigerer Ebene", sogenannte maschinenorientierte Sprachen. Dazu gehören Maschinensprachen und Assemblersprachen. Vereinfacht gesprochen können Computer nur Programme in Maschinensprache ausführen. D. h., dass Programme, die in einer höheren Programmiersprache geschrieben sind, zunächst übersetzt werden müssen, bevor sie ausgeführt werden können. Diese Übersetzung benötigt etwas Zeit, was einen kleinen Nachteil höherer Programmiersprachen darstellt.
Aber ihre Vorteile sind gewaltig. Erstens ist es viel leichter in einer höheren Programmiersprache zu programmieren; mit "leichter" meine ich: ein Programm zu schreiben benötigt weniger Zeit, es ist kürzer und leichter zu lesen und es ist mit höherer Wahrscheinlichkeit korrekt. Zweitens sind höhere Programmiersprachen portierbar. D. h., dass sie auf verschiedenen Arten von Computern laufen können ohne - oder mit nur wenigen - Änderungen. Maschinenorientierte Sprachen können nur auf einer Art von Computer laufen und müssen für andere Computertypen neu geschrieben werden.
Wegen dieser Vorteile werden heute fast alle Programme in höheren Programmiersprachen geschrieben. Maschinenorienterte Sprachen werden nur für wenige sehr spezielle Anwendungen verwendet.
Es gibt zwei Arten von Programmen, die höhere Programmiersprachen in maschinenorientierte Sprachen übersetzen: Interpreter und Compiler. Ein Interpreter ist ein Programm, das ein Programm in einer höheren Programmiersprache liest und ausführt, was es sagt. Er übersetzt das Programm zeilenweise, indem er abwechselnd eine Zeile liest und die Befehle, die dort stehen ausführt.
Ein Compiler ist ein Programm, das ein Programm in einer höheren Programmiersprache liest und als ganzes übersetzt, bevor irgendwelche Befehle ausgeführt werden. In diesem Fall nennt man das in der höherenProgrammiersprache geschriebene Programm den Quellcode und das übersetzte Programm den Objektcode oder executable. ist ein Programm einmal kompiliert, kann man es wiederholt ohne weitere Übersetzung ausführen.
Python wird als eine Interpretersprache betrachtet, weil Python-Progamme von einem Interpreter ausgeführt werden. Es gibt zwei Arten, den Interpreter zu benutzen: den Kommandozeilen-Modus und den Script-Modus. Im Kommandozeilen-Modus tippst du Python-Anweisungen ein und der Interpreter gibt die Ergebnisse aus.
$ python
Python 2.4.2 (#2, Sep 30 2005, 21:19:01)
[GCC 4.0.2 20050808 (prerelease) (Ubuntu 4.0.1-4ubuntu8)] on linux2
Type "copyright", "credits" or "license()" for more information.
>>> print 1 + 1
2
>>>
Die erste Zeile dieses Beispiels ist der Befehl, der den Python-Interpreter startet. Die nächsten zwei Zeilen sind Meldungen des Interpreters. Die dritte Zeile beginnt mit >>>, das ist der "Prompt", mit dem der Interpreter anzeigt, das er bereit ist, Eingaben entgegenzunehmen. Wir haben print 1 + 1 eingetippt und der Interpreter antwortete mit 2.
Alternativ können wir ein Programm in eine Datei schreiben und den Interpreter dazu benutzen, den Inhalt dieser Programm-Datei auszuführen. Eine solche Programm-Datei nennt man auch ein Script. Wir haben z. B. einen Texteditor benutzt, um eine Datei mit dem Namen latoya.py zu erzeugen, die folgenden Inhalt hat:
print 1 + 1
Vereinbarungsgemäß haben Dateien, die Python-Programme enthalten Namen, die mit .py enden.
Um das Programm auszuführen, muss man dem Interpreter den Namen des Scripts mitteilen.
$ python latoya.py
2
In anderen Programmierumgebungen können die Details der Programmausführung etwas anders sein. Außerdem sind die meisten Programme interessanter als dieses hier.
Die meisten Beispiele in diesem Kurs werden von der Kommandozeile aus ausgeführt. Die Arbeit auf der Kommandozeile ist bequem für die Programmentwicklung und für das Testen, da man Pythonanweisungen eintippen und unmittelbar ausführen kann. Wenn man einmal ein funktionierendes Programm hat, wird man es in einem Script abspeichern, damit man es später immer wieder ausführen oder auch abändern kann.
Ein Programm ist eine Folge von Anweisungen, die angeben, wie eine "Berechnung" - computation - durchzuführen ist. Diese "Berechung" kann etwas mathematisches sein, wie z.B. das Lösen eines Systems von Gleichungen oder die Berechnung der Nullstellen eines Polynoms, aber sie kann auch eine symbolische Berechnung sein, wie etwa suchen und ersetzen von Text in einem Dokument oder - seltsam genug - das Kompilieren eines Programms.
Die Details sehen in verschiedenen Progammiersprachen verschieden aus. Es gibt aber ein paar grundlegende Funktionen, die in jeder Programmiersprache vorkommen (müssen):
Ob du es glaubst oder nicht, das ist im Grunde alles was man braucht. Jedes Programm, das du jemals benutzt hast, ganz egal wie kompliziert es war, wird aus Funktionen aufgebaut, die mehr oder weniger wie diese aussehen. Daher kann man Programmieren so beschreiben: Programmieren ist ein Prozess, in dem man große, komplexe Aufgaben in kleine und immer kleinere Teilaufgaben zerlegt, bis schließlich die Teilaufgaben einfach genug sind, um mit einer dieser einfachen Funktionen ausgeführt werden zu können.
Das wird dir alles ein bisschen vage vorkommen. Aber wir kommen auf dieses Thema später zurück, wenn wir über Algorithmen sprechen werden.
Programmieren ist ein komplexer Prozess; weil es von Menschen ausgeführt wird, treten häufig Fehler auf. Merkwürdigerweise werden Programmierfehler als bugs bezeichnet. Dementsprechend wird der Vorgang der Fehlersuche und der Fehlerbehebung als debugging bezeichnet.
Es gibt drei verschiedene Arten von Fehlern, die in einem Programm auftreten können. Um sie schneller auffinden zu können ist es nützlich, zwischen ihnen zu unterscheiden.
Python kann ein Programm nur übersetzen, wenn das Programm syntaktisch korrekt ist; andernfalls schlägt der Vorgang fehl und es wird eine Fehlermeldung ausgegeben. Syntax bezieht sich auf die Struktur eines Programms und auf die Regeln für diese Struktur. Zum Beispiel muss im Deutschen ein Satz mit einem Großbuchstaben beginnen und mit einem Punkt enden. dieser Satz enthält einen Syntaxfehler. Und dieser auch
Für die meisten Leser stellen ein paar Syntaxfehler kein Problem dar, weswegen wir auch moderne Lyrik, zum Beispiel von E. E. Cummings, lesen können ohne dauernd Syntaxfehlermeldungen auszuspucken (Siehe Anhang E). Python ist nicht so nachsichtig. Wenn auch nur ein einziger Syntaxfehler in deinem Programm ist, wird Python eine Fehlermeldung ausgeben und seine Tätigkeit - die Übersetzung - abbrechen. Dein Programm kann dann nicht ausgeführt werden. Während der ersten Wochen deiner Programmierer-Karriere wirst du wahrscheinlich viel Zeit damit zubringen, deine Syntax-Fehler zu suchen (und hoffentlich zu finden). Mit zunehmender Erfahrung wirst du immer weniger Fehler machen und sie auch schneller finden.
Die zweite Art von Fehler sind Laufzeitfehler. Sie heißen so, weil sie erst auftreten, wenn das Programm ausgeführt wird. Diese Fehler werden auch Ausnahmen (englisch: exceptions) genannt, weil sie normalerweise vorkommen, wenn etwas ungewöhnliches (englisch: exceptional) (und schlimmes) eingetreten ist.
Für die einfache Art von Programmen, die wir in den nächsten paar Wochen schreiben werden, sind Laufzeitfehler selten. Daher wird es vielleicht noch eine Weile dauern, bis dir die ersten unterkommen.
Der dritte Typ von Fehler ist der logische oder semantische Fehler. Wenn dein Programm einen logischen Fehler enthält, wird es erfolgreich kompiliert und ausgeführt werden können in dem Sinn, dass der Computer keine Fehlermeldungen erzeugt. Aber es wird nicht das Richtige tun, sondern etwas anderes. Insbesondere wird es das tun, was du ihm aufgetragen hast zu tun.
Das Problem ist, dass das Programm, das du geschrieben hast nicht das Programm ist, das du schreiben wolltest (oder solltest). Die Bedeutung des Programms (seine Semantik) ist falsch. Logische Fehler zu identifizieren kann schwierig sein. Es erfordert, dass du ausgehend von der Ausgabe des Programms rückwärts arbeitest und so versuchst herauszufinden, was es wirklich tut.
Eine der wichtigsten Fertigkeiten, die du dir während dieses Kurses aneignen wirst ist debugging - die Fehlersuche. Obwohl es frustrierend sein kann, ist debugging eine der intellektuell anspruchsvollsten, herausforderndsten und interessantesten Teile des Programmierens.
In mancher Hinsicht ist debugging wie Detektivarbeit. Du hast einige Indizien (Hinweise) zur Verfügung und du musst auf die Vorgänge zurückschließen, die zu den Ergebnissen führen, die du siehst.
Debugging ist auch wie eine experimentelle Wissenschaft. Hast du erst einmal eine Idee, was falsch läuft, kannst du dein Programm abändern und neuerlich versuchen. War deine Hypothese korrekt, dann kannst du das Ergebnis der Abänderung voraussagen und du bist einen Schritt näher an einem korrekt arbeitenden Programm. War deine Hypothese falsch, musst du eine neue aufstellen. Wie Sherlock Holmes sagte: "When you have eliminated the impossible, whatever remains, however improbable, must be the truth." (A. Conan Doyle, The Sign of Four)
Für manche Leute ist Programmieren und Debugging dieselbe Sache: Programmieren ist der Prozess ein Programm Schritt für Schritt zu debuggen bis es das tut, was du willst. Dahinter steckt die Idee, dass du immer mit einem arbeitenden Programm beginnen solltest, das irgendetwas tut, und dann kleine Änderungen ausführst, die du während des Programmierens von ihren Fehlern befreist, so dass du immer ein lauffähiges Programm hast.
So ist z. B. Linux ein Betriebssystem, das Tausende Zeilen von Code enthält. Aber es begann als ein eifaches Programm, das Linus Thorvalds dazu benutzte, den Intel 80386-Chip zu erforschen. Larry Greenfield formulierte das so: "One of Linus's earlier projects was a program that would switch between printing AAAA and BBBB. This later evolved to Linux" (from The Linux Users' Guide Beta Version 1).
In späteren Kapiteln werde ich weitere Anregungen über Debugging und andere Programmiertechniken geben.
Natürliche Sprachen sind die Sprachen, die die Menschen sprechen, wie Englisch, Französisch und Deutsch. Sie sind nicht von Menschen entworfen worden (obwohl sie versuchen ihnen eine gewisse Ordnung aufzuerlegen); sie haben sich natürlich entwickelt.
Formale Sprachen sind Sprachen, die von Menschen für bestimmte Anwendungszwecke entworfen wurden. Z. B. ist die Formelschreibweise der Mathematik eine formale Sprache, die besonders gut geeignet ist, Beziehungen zwischen Zahlen und Symbolen auszudrücken. Chemiker benutzen eine formale Sprache um die chemische Struktur von Molekülen darzustellen. Und am wichtigsten für uns:
Programmiersprachen sind formale Sprachen, die entworfen wurden um Berechnungen (computations) auszudrücken.
Wie ich oben erwähnt habe, haben formale Sprachen ziemlich strikte Regeln für ihre Syntax. Zum Beispiel ist 3+3=6 ein syntaktisch korrekter mathematischer Ausdruck, 3=+6$ aber nicht. H2O ist ein syntaktisch korrekter chemischer Name, im Gegensatz zu 2Zz.
Es gibt zwei Arten von Syntax-Regeln: solche, die sich auf sogenannte token beziehen und solche die sich auf die Struktur eines Programms beziehen. Token sind die Elementarbestandteile der Sprache, wie Wörter und Zahlen und chemische Elemente. Eines der Probleme bei 3=+6$ ist, dass $ kein legales Token in der Mathematik ist (soweit ich weiß). Ähnlich ist 2Zz nicht legal, weil es kein Element mit der Abkürzung Zz gibt.
Die zweite Art von Syntax-Fehler bezieht sich auf die Struktur (die Gestalt) einer Anweisung; d. h. auf die Art und Weise, wie die Token angeordnet sind. Die Zeichenfolge 3=+6$ ist strukturell illegal, weil ein Plus-Zeichen nicht unmittelbar nach einem Gleichheitszeichen auftreten darf. Ähnlich müssen chemische Formeln ihre Indizes nach dem Elementnamen haben und nicht davor.
Als Übung erzeuge einen Satz der wie ein wohlstrukturierter deutscher Satz aussieht, der aber unerkennbare Token enthält. Dann schreibe einen anderen Satz auf, mit ausschließlich gültigen Tokens, aber ungültiger Struktur.
Wenn du einen deutschen Satz liest oder eine Anweisung in einer formalen Sprache, musst du herausfinden, was die Struktur des Satzes ist (obwohl man das bei einer natürlichen Sprache in der Regel unbewusst tut). Dieser Vorgang heißt parsen.
Wenn du z. B. den Satz hörst: "Der andere Schuh fiel, " so verstehst du, dass "der andere Schuh" das Subjekt ist und "fiel" das Prädikat. Wenn du einmal einen Satz "geparst" hast, also seine Struktur erkannt hast, kannst du seine Bedeutung ermitteln, d. h. seine Semantik. Angenommen, du weißt was ein Schuh ist, und was es bedeutet zu fallen, wirst du die allgemeine Aussage dieses Satzes verstehen.
Obwohl formale und natürliche Sprachen viele Eigenschaften gemeinsam haben -- Token, Struktur, Syntax, Semantik -- gibt es viele Unterschiede:
Alle Menschen wachsen mit natürlichen Sprachen auf, und vielen fällt es schwer sich an formale Sprachen zu gewöhnen. In mancher Hinischt ist der Unterschied zwischen formaler und natürlicher Sprache wie der Unterschied zwischen Prosa und Lyrik:
Hier einige Anregungen für das Lesen von Programmen (und anderer formaler Sprachen). Erstens bedenke, dass formale Sprachen viel dichter sind, als natürliche Sprachen; also braucht man länger, um sie zu lesen. Zweitens ist die Struktur sehr wichtig. Daher ist es im Allgemeinen keine gute Idee, ein Programm von oben nach unten und von links nach rechts zu lesen. Statt dessen lerne, das Programm in deinem Kopf zu "parsen", die Token zu identifizieren und die Struktur zu interpretieren. Drittens bedenke, dass es auf Details ankommt. Kleine Dinge, wie Tippfehler und falsche Zeichensetzung, die bei natürlicher Sprache keine große Rolle spielen, können in einer formalen Sprache einen großen Unterschied ausmachen.
Traditionellerweise ist das erste Programm, das man in einer neuen Sprache schreibt das "Hello, World!" - Programm. Es tut nichts anderes, als die Wörter "Hello, World!" auszugeben. In Python schaut dieses Programm so aus:
print "Hello, World!"
Das ist ein Beispiel einer print - Anweisung, die aber nichts auf Papier druckt. Sie zeigt einen Wert auf dem Bildschirm an. In diesem Beispiel besteht das Ergebnis aus den Worten
Hello, World!
Die Anführungszeichen markieren in dem Programm nur den Beginn und das Ende des Wertes, sie werden im Ergebnis nicht dargestellt.
Manche Leute beurteilen die Qualität einer Programmiersprache danach, wie einfach das "Hello, World." - Programm ist. Gemessen an diesem Kriterium schneidet Python ziemlich optimal ab.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|