Was werden wir behandeln? |
---|
Das vierte Element der Programmierung ist die modulare Programmierung. Tatsächlich ist diese nicht unbedingt erforderlich, und wenn du verwendest, was wir bisher durchgenommen haben, dann kannst du schon recht nette, eindrucksvolle Programme schreiben. Dennoch, wenn die Programme größer werden, wird es immer schwerer und schwerer den Überblick zu behalten, über das, was wo abläuft. Wir benötigen dringend eine Möglichkeit einige Einzelheit weg zu abstrahieren, so dass wir allein über unser zu lösendes Problem nachdenken müssen, nicht über minutiöse Einzelheiten, wie der Computer arbeitet. In einem gewissen Umfang ist es das, was Python, BASIC etc. schon für uns mit ihren eingebauten Fähigkeiten tun - sie bewahren uns davor, uns mit der Hardware des Computers abgeben zu müssen, wie das Lesen der einzelnen Tasten der Tastatur usw.
Die Rolle der modularen Programmierung ist es, dem Programmierer zu ermöglichen, die eingebauten Fähigkeiten der Programmiersprache zu erweitern. Dabei werden Programmstücke innerhalb von Modulen eingepackt, die wir dann in unsere Programme 'einstecken' können. Die erste Form von Modul war das Unterprogramm (Subroutine), welche eine Codeblock war, zu dem man hinspringen konnte (vergleichbar dem GOTO, das im Kapitel über Verzweigungen erläutert wurde); wenn der Block erledigt war, wurde wieder zu der Stelle zurückgesprungen, von der es aufgerufen wurde. Dieser besondere Stil der Modularität ist bekannt als Prozedur oder Funktion. In Python und einigen anderen Sprachen hat das Wort Modul eine spezifische Bedeutung, die wir kurz betrachten werden, aber lass uns zuvor die Funktionen etwas näher anschauen.
Bevor die Erzeugung von Funktionen erläutert wird, lass uns untersuchen, wie wir die vielen, vielen Funktionen verwenden können, die bei jeder Programmiersprache dabei sind (oftmals Bibliothek oder Library genannt).
Wir haben schon einige Funktionen im Gebrauch und andere im Abschnitt über die Operatoren aufgelistet. Jetzt werden wir beleuchten, was diese gemeinsam haben und wie wir sie in unseren Programmen nutzen können.
Die Basisstruktur einer Funktion ist wie folgt:
einWert = irgendeineFunktion(einArgument, einanderes, usw...)
Dies ist eine Variable, die einen Wert durch einen Funktionsaufruf erhält. Die Funktion kann 0 oder viele Argumente akzeptieren, die sie wie interne Variablen behandelt. Funktionen können intern andere Funktionen aufrufen. Lass uns ein paar Beispiele in unseren verschieden Sprachen betrachten, um zu sehen, wie das funktioniert:
Dies druckt die folgenden m Zeichen beginnend beim nten in str$. (Erinnere dich, dass Namen, die in BASIC mit '$' enden, einen String kennzeichnen.)
time$ = "MORGEN MITTAG ABEND" PRINT "Guten";MID$(time$,7,7)
Dies druckt "Guten MITTAG" aus.
Dies gibt die spezifizierte Umgebungsvariable str$ zurück.
PRINT ENVIRON$("PATH")
Druckt den momentanen, in DOS gesetzten PATH (gewöhnlich über die autoexec.bat-Datei) aus.
Gibt die Länge der Liste L zurück.
set a {"Erster" "Zweiter" "Dritter"} # 3 Element-Liste puts [llength $a] # die Ausgabe ist '3'
Anmerkung: Nahezu alles in Tcl ist eine Funktion (oder wie Tcl es zu bezeichnen pflegt, ein Kommando). Dies führt zu einer grausigen Syntax , macht es aber dem Computer besonders leicht, Tcl-Programme zu lesen. Dies ist wichtig, weil Tcl für den Ausdruck Tool Control Language (Werkzeugkontrollsprache) steht und entwickelt wurde, um in andere Programme als Makrosprache eingebettet zu werden, wie Visual Basic for Applications(VBA) in Microsoft-Produkten. Du kannst dies auch in Python auf dieselbe Art einbetten, aber Tcl ist einzigartig in dem, wozu es geschaffen wurde, nämlich wegen des Einbettens.
x = 2 # wir werden 2 als unsere Basiszahl verwenden for y in range(0,11): print pow(x,y) # berechnet 2 hoch y, hier 0-10
Hier generieren wir Werte von y mit den Werten 0 bis 10 und rufen die eingebaute pow()-Funktion mit 2 weitergegebenen Argumenten auf: x und y. Bei jedem Aufruf werden die momentanen Werte von x und y innerhalb des Aufrufes ersetzt und das Ergebnis wird gedruckt.
Anmerkung: Der Exponentialoperator ** ist äquivalent zur pow()-Funktion.
Eine andere nützliche in Python eingebaute Funktion ist dir, die beim Weitergeben eines Modulnamens eine Liste der gültigen Namen zurückgibt - meist der Funktionen - in diesem Modul. Probier es mit eingebauten Funktionen:
print dir(__builtin__)
Anmerkung: Um dies auf beliebige andere Module anwenden zu können, mußt du das Modul zuerst mit import importieren, andernfalls wird Python bemängeln, dass es den Namen nicht zuordnen kann.
Bevor wir etwas anderes machen werden, sollten wir jetzt besser mehr im Detail über Python-Module reden.
Python ist eine extrem erweiterungsfähige Sprache (genauso wie Tcl), in die du zusätzliche Fähigkeiten durch den import von Modulen einbringen kannst. Wir werden in Kürze sehen, wie man Module erzeugt, aber jetzt werden wir mit einigen Standardmodulen herumspielen, die mit Python ausgeliefert werden.
Wir haben sys schon getroffen, als wir es für exit aus Python verwendet haben. Es beinhaltet ein ganzes Bündel mit noch anderen nützlichen Funktionen. Um diese zugänglich zu machen, müssen wir ein import sys durchführen:
import sys # macht Funktionen zugänglich sys.exit() # Präfix 'sys.'
Falls wir wissen, dass wir die Funktionen eines Moduls sehr oft verwenden werden und wir sonst nicht die gleichen Funktionsnamen importiert oder erzeugt haben, dann können wir dies tun:
from sys import * # importiert alle Namen von sys exit() # kann jetzt ohne spezifizierendes Präfix 'sys' benutzt werden
Wir könne alle Pythonmodule auf diese Art und Weise importieren und verwenden, und das betrifft auch die von dir selbst erzeugten . Wir werden bald sehen, wie man das macht. Zuerst aber machen wir eine schnelle Tour durch einige Python-Standardmodule und etwas von dem, was sie zu bieten haben:
Modulname | Beschreibung |
---|---|
sys | Erlaubt die Interaktion mit dem Pythonsystem: |
os | Erlaubt die Interaktion mit dem Betriebssystem: |
string | Erlaubt die Manipulation von Strings |
re | Erlaubt die Manipulation von Strings in reguläre
Ausdrücke im Unix-Stil |
math | Erlaubt den Zugang zu vielen mathematischen
Funktionen: |
time | Zeit-(und Datums-) Funktionen |
Diese ist nur die Spitze des Eisberges. Python ist tatsächlich mit Dutzenden von Modulen ausgestattet, und du kannst auch viele runterladen. (Eine gute Quelle ist Vaults of Parnassus.) Sieh dir die Dokumentationen an und finde etwas heraus über Internet-Programmierung, Graphik, Aufbau von Datenbanken etc.
Als wichtige Sache sollte man sich vergegenwärtigen, nämlich dass die meisten Programmiersprachen diese Grundfunktionen eingebaut oder als Teil ihrer Standardbibliothek besitzen. Überprüfe die Dokumentation, bevor du eine Funktion schreibst - es könnte sie schon geben! Dies führt uns fein zum...
OK, wir wissen jetzt, wie man existierende Funktionen verwendet, aber wie erzeugen wir eine neue Funktion? Einfach indem wir sie deklarieren. Das geschieht durch das Schreiben einer Anweisung, die dem Interpreter erzählt, dass wir einen Codeblock definieren wollen, den es überall in unserem Program auf Verlangen einsetzt.
So lass uns einmal eine Funktion erzeugen, die eine Multiplikationstabelle für jedes von uns vorgegebene Argument ausdruckt. In BASIC sieht das so aus:
SUB VIELFACH (N%) FOR I = 1 TO 12 PRINT I; "x"; N%; "="; I * N% NEXT I END SUB
Und wir können das aufrufen mit:
PRINT "Hier ist die 7er Tabelle..." VIELFACH(7)
Anmerkung: Wir definierten einen Parameter , genannt N% und übergaben ihm das Argument 7 . Die lokale Variable N% innerhalb der Funktion nimmt den Wert 7 an, wenn wir sie aufrufen. Wir dürfen so viele Parameter in der Funktionsdefinition definieren, wie wir möchten und die aufrufenden Programme müssen für jeden Parameter Werte vorgeben. Einige Programme erlauben es dir, Ersatzwerte (Defaultwerte) für einen Parameter vorzugeben, so daß, falls kein anderer Wert angeben wird, die Funktion den Ersatz nimmt. Wir werden dies später in Python sehen.
In Python sieht die VIELFACH-Funktion so aus:
def vielfach(n): for i in range(1,13): print "%d x %d = %d" % (i, n, i*n)
Und wird so aufgerufen:
print "Hier ist die 9er Tabelle..." vielfach(9)
Beachte, dass diese Funktionen keine Werte zurückgeben (Sie sind in Wirklichkeit das, was einige Sprachen Prozeduren nennen). Deshalb kannst du feststellen, dass die BASIC-Version tatsächlich das Schlüsselwort SUB anstelle von FUNCTION verwendet. Dies steht für Subroutine, ein aus der Zeit der Assemblerprogrammierung verwendeter Begriff, der in BASIC eine Funktion meint, die keine Werte zurückgibt. Im Gegensatz dazu verwendet Python den Begriff def, der die Kurzform für 'define' (definiere) , so dass folglich angenommen wird, dass es sich um eine Funktion handelt.
Erinnerst du dich, dass ich den Gebrauch von Defaultwerten erwähnt habe? Einen sinnvollen Gebrauch erfolgt in einer Funktion, die den Wochentag zurückgibt. Wenn wir sie ohne Wert aufrufen, meinen wir den heutigen Tag, andernfalls übergeben wir eine Tageszahl. Etwa so:
# TagesZahl von -1 => heute def wochenTag(TagesZahl = -1): tage = ['Montag','Dienstag', 'Mittwoch','Donnerstag', 'Freitag', 'Samstag', 'Sonntag'] # überprüfe den Ersatzwerte if TagesZahl == -1: # Verwende die time-Module-Funktionen um die momentane Zeit zu erhalten # siehe obige Tabelle und die offizielle Modul-Dokumentation import time diezeit = time.localtime(time.time()) TagesZahl = diezeit[6] return tage[TagesZahl]
Anmerkung: Wir benötigen das time-Modul nur dann, wenn der Defaultwert in Anspruch genommen wird; deshalb verschieben wir die Import-Operation , bis wir sie brauchen. Dies führt zu einer leichten Ausführungsbeschleunigung, wenn wir die Defaultwertfähigkeit unsererer Funktion nicht verwenden.
Jetzt können wir dies aufrufen mit:
print "Heute ist: %s" % wochenTag()
# erinnere dich, dass wir in der Computersprache mit 0 zu zählen beginnen
# und in diesem Falle nehmen wir den Montag als ersten Tag an.
print "Der dritte Tag ist %s" % wochenTag(2)
Wieder zurück zur Multiplikation....
Was ist, wenn wir eine Funktion definieren möchten, die die Werte der Multiplikation als ein Feld (Array) zurückgibt? So sieht das in BASIC aus:
FUNCTION VIELFACH% (N%) DIM WERTE(12) AS INTEGER FOR I = 1 to 12 WERTE(I) = I*N% NEXT I RETURN WERTE END FUNCTION
Und in Python:
def vielfach(n):
# erzeuge neue leere Liste
werte = []
for i in range(1,13):
werte.append(i*n)
return werte
Das wäre ziemlich dumm, weil es einfacher ist, direkt auf Wunsch i*n zu berechnen. Aber hoffentlich erkennst du die Idee. Eine praktischere Funktion, die einen Wert zurückgibt, mag eine sein, die die Anzahl der Wörter in einem String zählt.Du kannst sie verwenden, um die Wörter in einer Datei zu zählen, durch addieren der Gesamtzahlen in jeder Zeile.
Der Code dafür könnte etwa so aussehen:
def numworte(s): list = split(s) # Liste mit jedem Wort als Element return len(list) # Rückgabe der Elementzahl in der Liste for line in file: total = total + numworte(line) # accumuliert Gesamtzahl für jede Zeile print "Die datei hat %d Wörter" % total
Wenn du das jetzt ausprobierst, merkst du, dass dies nicht funktioniert. Ich haben hier eine allgemeine Design-Technik verwandt, die lediglich skizziert, wie meines Erachtens der Code in etwa aussehen könnte und es nicht schwer macht, daraus den absolut korrekten Code zu erstellen. Dies ist manchmal bekannt als Pseudo Code oder in einem etwas formaleren Stil Program Description Language (PDL:"Programmbeschreibungssprache").
Bald werden wir eine etwas nähere Betrachtung vom Umgang mit Dateien und Strings haben; etwas später in diesem Kurs werden wir auf dieses Beispiel zurückkommen und es richtig machen.
Wir können natürlich Tcl-Funktionen erzeugen, und wir tun das unter Verwendung des proc Kommandos, etwa so:
proc vielfach {m} { for {set i 1} {$i <= 12} {incr i} { lappend results [expr $i * $m] } return $results }
Beachte, dass ich automatisch eine Liste mit dem Namen results erzeuge, sobald ich das Tcl-lappend verwende.
Tcl ist ein bisschen anders in der Art wie es mit Funktionen umgeht. Sicherlich hast du schon festgestellt, dass ich die eingebauteten Funktionen Kommandos genannt habe. Das ist deshalb so, weil in Tcl jedes beim Promptzeichen eingegebene Kommando tatsächlich ein Funktionsaufruf ist. Die meisten Sprachen kommen mit einem Satz von Schlüsselwörtern (keywords), wie for, while, if/else und so weiter daher. Tcl macht alle diese Konrollschlüsselwörter und Kommandos zu Funktionen. Das hat den interessanten, verwirrenden und sehr starken Effekt, uns zu erlauben, eingebaute Kontrollstrukturen etwa wie folgt umzudefinieren:
set i 3 while {$i < 10} { puts $i set i [expr $i + 1] }
Wie erwartet, druckt dies die Ziffern von 3 bis 9 (= 1 weniger als 10) aus. Aber lass uns jetzt unsere eigene Version des while-Kommandos redefinieren:
proc while {x y} { puts "Mein while jetzt" } set i 3 while {$i < 10} { puts $i set i [expr $i + 1] }
Dies tut nichts ausser lediglich die Meldung "My while now" zu drucken. Der Ausdruck und die Befehlsabfolge werden ignoriert, weil TCL sie als Parameter der while-Funktion behandelt und die while-Funktion erwartet sie, aber ignoriert sie! Du kannst also sehen, wie wir Prozeduren in Tcl definieren und wie wir dies missbrauchen können, um sehr verwirrende Programme zu erzeugen - tue dies nicht, ohne einen äußerst guten Grund zu haben!
Wir können jetzt also unsere eigenen Funktionen erstellen und diese von anderen Stellen in unserem Programm aus aufrufen. Das ist deshalb gut, weil es uns eine Menge an Tipparbeit erspart und, viel wichtiger, unsere Programme viel leichter verständlich macht, weil wir einiges über die Details vergessen können, nachdem wir die Funktion erschaffen haben, die sie versteckt. (Das Prinzip des Verpackens von komplexen Teilen eines Programmes innerhalb einer Funktion wird aus ziemlich offensichtlichen Gründen als Verstecken von Information bezeichnet.) Aber wie können wir diese Funktionen in anderen Programmen verwenden? Wir erzeugen ein Modul.
Ein Modul in Python ist nichts Besonderes. Es ist lediglich eine schlichte Textdatei voll mit Python-Programmanweisungen. Normalerweise sind diese Anweisungen Funktionsdefinitionen. Das ist so, wenn wir eingeben:
from sys import *
Im Endeffekt kopieren wir die Inhalte von sys.py in unser Programm hinein, genau so wie mit einer Ausschneide-und-Einfüge-Operation. (Es ist nicht genau, aber vom Konzept her so.) In der Tat kopiert in einigen Programmiersprachen (bemerkenswerterweise C++) der Übersetzer einfach direkt Moduldateien in das laufende Programm, wenn diese erforderlich sind.
Also zum Rekapitulieren: Wir erzeugen ein Modul dadurch, indem wir eine Python-Datei erzeugen, die die Funktionen enthält, die wir in anderen Programmen wiederverwenden wollen. Dann importieren wir unser Modul genauso, wie die Standardmodule. Einfach, nicht? Dann machen wir das doch 'mal.
Kopiere selbst die untere Funktion in eine Datei und speichere die Datei mit dem Namen vielftab.py.
def print_tabelle(multiplikator): print "--- Drucke die %d -fach Tabelle ---" % multiplikator for n in range(1,13): print "%d x %d = %d" % (n, multiplikator, n*multiplikator)
Gib jetzt ins Python-Prompt ein:
>>> import vielftab >>> vielftab.print_tabelle(12)
Booah eehh ! Du hast ein Modul erzeugt und es benutzt.
Wichtige Anmerkung: Wenn du Python nicht vom gleichen Verzeichnis aus startest, wie demjenigen, in dem du die vielftab.py-Datei gespeichert hast, so wird Python die Datei möglicherweise nicht finden und einen Fehler melden. Falls das zutrifft, so hast du eine Umgebungsvariable zu erzeugen, PYTHONPATH genannt, die eine Liste von allen gültigen Verzeichnisse zur Modulsuche enthält (zusätzlich zu den von Python unterstützten Standardmodulen).
Die Erzeugung von Umgebungsvariablen ist eine plattformspezifische Operation, wobei ich annehme, dass du entweder weisst wie man sie durchführt oder es sonst woher herausfindest.
Was ist mit BASIC? Das ist komplexer.... In QBASIC und anderen älteren Erscheinungen besteht kein eigentliches Modul-Konzept. Du musst manuell von anderen Projekten benötigte Sachen kopieren und mit Hilfe deines Texteditors in dein aktuelles Projekt einfügen. Dennoch gibt es in Visual Basic ein Modulkonzept und du kannst ein Modul über das Integrated Development Environment (IDE) File|Open Module...-Menu laden. Dort gibt es einige Einschränkungen, welche Art von Dinge in einem BASIC-Module sein dürfen; da wir aber in diesem Kurs kein Visual Basic verwenden, würde das zu weit führen. (Anmerkung: Es gibt eine abgespeckte Version von Visual Basic, bekannt als die COM Controls Edition, CCE, frei runter zu laden bei Microsoft's website, falls du Lust zum Experimentieren hast. Auch Windows 98, 2000 und IE5 installieren alle eine verkürzte Version von VB, genannt VBScript, die man in Dateien mit der Endung .vbs verwenden kann.)
Endlich Tcl, wie immer(!), ist wieder irgendwie eklektisch, aber nichtsdestotrotz interessant, und führt uns zur Betrachtung von wiederverwertbaren Modulen (oder wie diese dort bevorzugt genannt werden, libraries - Bibliotheken).
Auf der einfachsten Stufe kannst du eine Datei mit Tcl-Funktionen erzeugen, wie wir es in Python getan haben und dann in deinem Programm diese Datei als source (Quelle) angeben. Dies veranlasst direkt den Interpreter deine Datei zu lesen und diese Programme werden für den Gebrauch zugänglich. Aber es gibt noch eine interessantere Option:
Du kannst deine Dateien wie oben erzeugen, steckst sie dann alle in ein Verzeichnis und lässt dann ein mk_index -Kommando ablaufen. Dieses bildet einen Index von allen Funktionen und Files im Verzeichnis. Dann rufst du in deinem Programm ganz einfach die erforderliche Funktion auf und der TCL Interpreter wird erkennen, dass die Funktion nicht zugänglich ist und schaut automatisch im Indexfile. Es wird dann die relevanten Source-Files aus der Bibliothek "sourcen" und die Funktion ausführen.
Eine einmal als Quelle definierte Funktion bleibt zugänglich, so dass diese bei allen weiteren Programmausführungen mitgeschleift wird. Der einzige Haken ist, dass der Programmierer mehr als eine Funktion mit dem gleichen Namen vermeiden muß. Diese Eigenschaft von Tcl ist bekannt als autoloading.
Beim Folgenden werden wir einen Blick auf das Handling von Dateien und Texten werfen und dann, wie schon angekündigt, das Zählen von Wörtern in einer Datei vornehmen. Letztendlich werden wir ein Modul mit Funktionen zur Behandlung von Texten rein zu unserem Vergnügen erzeugen.
Zur Erinnerung |
---|
Im Falle von Fragen oder Hinweisen sende eine Nachricht in
Englisch an den Autor: alan.gauld@yahoo.co.uk
oder in Deutsch an den Übersetzer bup.schaefer@freenet.de