Aus Linux-Magazin 02/2001

Standard oder nicht Standard, das ist hier die Frage

Der Scheme-Standard hat einen recht kleinen Umfang, also greifen Programmierer für größere Projekte zwangsläufig auf Erweiterungen zurück. Im Zentrum dieses Artikels stehen daher Standard-Scheme und seine Erweiterungen.

Vielleicht fragen Sie sich, warum sich Programmierer um Standards scheren sollten. Standards sind ganz allgemein kein Selbstzweck, sondern erlauben es dem Nutzer, eine gewisse Unabhängigkeit von Anbietern zu erhalten. Ich sehe mich auch bei Programmiersprachen in der Position eines Nutzers und binde mich daher ungern an Produkte, die nicht standardisiert sind oder die mir keinen Einfluss geben, etwaige Probleme umschiffen zu können. Stellen Sie sich vor, die Firma, derern Entwicklungswerkzeuge Sie einsetzen, verschwindet von Markt oder entschließt sich, das Produkt nicht weiterzuentwickeln. Selbst eine Verschärfung der Lizenzbedingungen kann schon fatale Folgen haben. Zur Lösung dieses Problems sind denkbar:

  • Nur Open-Source-Pakete nutzen (Python, Perl, Scheme, (X)Emacs, Eiffel, Common Lisp und viele mehr)
  • Programmwerkzeuge, die mehr als eine Implementierung haben (Eiffel, Common Lisp, Java, Scheme) und Möglichkeiten, auf einem Interpreter/Compiler geschriebene Progamme auf andere Implementierungen zu portieren.
  • Sprachen, die Standards besitzen, wovon es überraschenderweise wenige gibt (beispielsweise COBOL, C, C++, FORTRAN, Scheme, Common Lisp und ADA)

Scheme erfüllt alle drei Punkte; es gibt verschiedene kommerzielle und freie Implementierungen und es existiert ein – wenn auch kleiner – Standard.

Für “ernsthafte” Softwareerstellung jedoch ist man in Scheme wie auch in C auf Erweiterungen angewiesen. Dabei spielen Bibliotheken eine zentrale Rolle; die Slib ist Schemes “erweiterte Standardbibliothek”, die in (fast) alle Implementierungen eingesetzt werden kann. Zunächst werde ich auf Standardscheme eingehen, im folgenden dann auf Erweiterungen entweder von DrScheme oder der Slib.

Eingabe/Ausgabe

In allen bisherigen Beispielen wurde die Ausgabe von der read-eval-print-loop angestoßen. Diese Schleife nützt selbstverständlich die Basismöglichkeiten der Sprache für die eigene Ein- Ausgabe. Ein- und Ausgabequellen heißen in Scheme Ports. Die Standardports kann man in Scheme mit den Funktionen current-input-port und current-output-port herausfinden. Der Eingabeport ist normalerweise die Tastatur, der Ausgabeport der Bildschirm. Für die Eingabe stehen in R5RS Scheme zur Verfügung: read, read-char, eof-object?, peek-char und char-ready?. Für die Ausgabe: write, write-char, display und newline. Zum Schreiben und Lesen von Dateien offeriert Scheme die Funktionen: call-with-input-file, call-with-output-file, with-input-from-file, with-output-to-file, open-input-file, open-output-file, close-input-port und close-output-port .

Im Listing 1 wird die Datei mit dem Namen from zum Lesen geöffnet, die Datei to zum Schreiben. Der Rückgabewert der open-* Funktionen ist jeweils ein Port, was Sie mit den *-port? Methoden überprüfen können. Der Aufruf von read-char und write-char erfolgt mit dem optionalen Portargument, was die die Ein- und Ausgabe auf die mit den Ports assoziierten Dateien “umlenkt”. Das Beispiel-Programm kopiert die Datei Zeichen für Zeichen, und ist damit eine der ineffektivsten Methoden, um eine Datei zu kopieren. Wird das Ende der Datei erkannt, werden die Ports geschlossen. Die Rückgabewerte der close-* Funktionen sind unspezifiziert, daher erhalten Sie keine Rückmeldung über den Erfolg oder Misserfolg dieser Kopieraktion. Neben der dem zeichweisen Einlesen mit der Funktion read-char kann man mit read ganze Scheme-Programme einlesen .

(define (read-from-file from) 
  (call-with-input-file from 
    (lambda (port) 
      (eval (read port)))))

In diesem Beispiel erübrigt sich das explizite Öffnen und Schließen der Datei, da sich diese Aufgabe mittels der call-with-input-file-Funktion an Scheme delegieren lässt. call-with-input-file erwartet einen Dateinamen und eine Funktion mit einem Parameter (der dem geöffneten Port entspricht). Die Funktion öffnet die angegebende Datei, führt die Funktion aus und schließt den Port anschließend wieder.

Rufen wir diese Funktion einmal mit der folgenden Datei ( fact.scm )auf:

(define (fact n) 
  (if (= n 0)  
      1 
      (* n  (fact (- n 1))))) 
 
> (read-from-file "fact.scm") 
> (fact 10) -> 3628800

Mit read wird der in einer Datei stehende Scheme-Code eingelesen und geparst, mit eval ausgewertet, das Resultat dieser Auswertung steht Ihnen dann zur Verfügung. Auch das Kommandoprompt von Scheme arbeitet auf die gleiche Weise. Bitte probieren Sie es einfach einmal aus. Es handelt sich hierbei nicht um ein besonderes Merkmal von Scheme, sondern gilt auch für andere Lisps. Damit kennen Sie nun schon alle “Standardfunktionen” zur Ein- Ausgabe in Scheme. Kommen zusätzlich Dateien und Verzeichnisse ins Spiel, müssen Sie auf Erweiterungen Ihres Schemes und/oder Möglichkeiten der Slib zurückgreifen.

Listing 1: Beispiel für Eingabe/Ausgabe: Kopieren einer Datei

(define (my-copy-file from to) 
  ;; Standard Scheme bietet keine Möglichkeit zu testen 
  ;; ob Dateien existieren und/oder welche Rechte sie haben
  ;; diese Funktion arbeitet also nur zufällig :( 
  (let ((ip (open-input-file from)) 
        (op (open-output-file to))) 
    (do ((ch (read-char ip) (read-char ip))) 
      ((eof-object? ch)  
       (begin  
         (close-input-port ip) 
         (close-output-port op))) 
      (write-char ch op))))

Warnung: Wer dieses Programm unbearbeitet benutzt, wird mit Datenverlusten nicht unter 1 GB “bestraft” ;-).

Vektoren und Zeichenketten

Bisher haben wir uns nur am Rande mit Zeichenketten (Strings) beschäftigt. Scheme bietet auch Funktionen zur Bearbeitung von Zeichenketten an, die aber ebenfalls wieder fast nur die Basisfunktionalität abdecken. Zeichenketten sind in Scheme Vektoren oder Felder, sie sind wie in C Null-indiziert, das heißt, das erste Zeihen einer Zeichenkette findet man an der Position Null.

Einzelne Zeichen in Scheme werden mit #name dargestellt , so ist beispielsweise #a der Buchstabe ‘a’.

Zeichenketten kann man vergleichen, aneinanderhängen, aufsplitten, kopieren, in Listen umwandeln und/oder aus Listen generieren.

Die Arbeitsweise der Funktion in Listing 2 dürfte Ihnen keine Schwierigkeiten mehr bereiten. Die Funktion erwartet zwei Parameter; beim ersten handelt es sich um eine Funktion, die auf jeweils ein Zeichen angewandt wird, was man an dem Aufruf (fun ch) erkennen kann; beim zweiten um die Zeichenkette, die verarbeitet werden soll. str-len ermittelt die Länge der übergebenen Zeichenkette. Ein neuer String der gleichen Länge wird angelegt und zum Rückgabewert der Funktion gemacht. Die do Schleife durchläuft die übergebende Zeichenkette, wendet die übergebende Funktion auf jedes einzelne Zeichen an und schreibt das Ergebnis der Funktion an die entsprechende Stelle des Rückgabewertes (beachten Sie die Initialisierung von i mit 0!). Sie können diese Funktion so anwenden:

(string-to char-upcase "was_auch_immer") 
-> WAS_AUCH_IMMER

“Unüblicher” Kontrollfluß

Kommen wir zu einem “berühmt-berüchtigten” Merkmal von Scheme , call-with-current-continuation (im folgenden mit call/cc ) abgekürzt.

Call/cc zeigt, wie diverse Elemente aus anderen Sprachen durch ein einzelnes Konstrukt ersetzt werden können, wie etwa goto, continue, break, setjmp, longjmp, sigsetjmp aus C oder try … except , break, continue aus Python.

Wie bei allen Sprüngen ist der Grat zum “Spaghetticode” ziemlich schmal.

Betrachten wir in Listing 3 einmal verschiedene Aufrufe von foo und untersuchen saved-continuation.

> (foo 102) -> 103 
> (foo 103) -> 104 
> saved-continuation -> #f 
> (foo 50) -> -49! 
> saved-continuation -> #continuation 
> (saved-continuation 100) -> 101 
> (saved-continuation 50) -> 51

Zunächst definieren wir eine globale Variable, an die wir in einem bestimmten Fall eine “continuation” (Fortsetzung, Weiterführung) binden. Was soll nun “weitergeführt” werden? Call/cc steht in einem Kontext der Addition. call/cc führt dazu dass dieAuswertung des zweiten Summanden offen bleibt, die Situation stellt sich also folgendermaßen dar: (+ 1 r) , das leere Kästchen steht für eine noch nicht bekannte Fortführung, die continuation.

Die Weiterführung im ersten Teil der Fallunterscheidung besteht darin, einfach den übergebenen Parameter einzusetzen (also (+ 1 num ) ) zu berechnen; im else-Teil der Fallunterscheidung tritt aber die call/cc in Aktion. Die Weiterführung exit-fun (eine Funktion mit einem Parameter) wird aufgerufen und alle weiteren Aktionen ausgeschlossen. Somit kommt die Addition (+ 1 2) nicht mehr zur Geltung. Da die “Ausstiegsfunktion” an die global deklarierte Variable saved-continuation gebunden wird, können wir diese Aktion an beliebiger Stelle weiterführen, wie man an den Aufrufen (saved-continuation 100) und (saved-continuation 50) erkennen kann. Die Charakteristik von call/cc entspricht somit der Definition einer Sprungmarke in Verbindung mit einer Funktion um diese Sprungmarke aufzurufen – zusätzlich speichert es die Umgebung (stack, etc.) der Funktion! Fast keine Programmiersprache bietet etwas in dieser Form.

Listing 2: Funktionen für Zeichen und Zeichenkette

(define (string-to fun str) 
  (let* ((str-len (string-length str)) 
         (result (make-string str-len))) 
    (do ((i 0 (+ i 1))) 
      ((> i (- str-len 1)) result) 
      (let ((ch (string-ref str i))) 
        (string-set! result i (fun ch))))))

Listing 3: call/cc in Aktion

(define saved-continuation #f) 
(define (foo num) 
  (+ 1 (call/cc  
        (lambda (exit-fun) 
          (if (> num 100) 
              num 
              (begin 
                (set! saved-continuation exit-fun) 
                (exit-fun (* -1 num)) 
                (+ 1 2)))))))

Listing 4: Alists

(define my-person '((vorname . "Friedrich") 
                    (name . "Dominicus") 
                    (job . "Programmierer (manchmal)"))) 
 
(define (person-name person) 
   (cdr (assq 'name person))) 
(define (set-person-name! person name) 
   (set-cdr! (assq 'name person) name))

Nachtrag Nummer Eins AList

Ein aufmerksamer Leser des ersten Teils dieser Artikelreihe hat mich auf mein Versäumnis bezüglich einer Datenstruktur hingewiesen, die ich daher an dieser Stelle noch vorstelle. Eigentliche Listen existieren in Scheme nicht, sondern werden aus einer Reihe von Paaren (der car- und cdr-Zelle) gebildet.

Sie könen allerdings alle möglichen Elemente an Stelle der cdr-Zelle einsetzen, somit auch weitere Paare, die dann eine Liste bilden.

Mit Paaren kann man daher nicht nur Listen aufbauen sondern auch Assoziationslisten, die man mit Hash Tabellen vergleichen kann.

Die neuen Funktionen in Listing 4 sind assq und set-cdr!. Die erste Funktion “sucht” nach dem Eintrag und liefert das Paar (Schlüssel . Wert) zurück, das erste Element des Paares ist also der Schlüssel. Mit set-cdr! wird der cdr Teil einer Liste oder Paars verändert. Der Punkt zwischen dem Schlüssel und dem Wert zeigt an, dass es sich nicht um eine Liste, sondern um ein Paar handelt. In diesem Beispiel ist der Wert nur eine einfache Zeichenkette, sie können selbstverständlich auch eine Liste als zweites Element angeben, eine Beispiel dazu finden Sie in den Quellen dieses Artikels. (Datei alist-and-hash-table.scm)

Listing 5: Eine “verbesserte” Kopierfunktion (MzScheme Erweiterungen)

(define (open-files from to) 
  (unless (file-exists? from)  
    (error (string-append "Kann die Datei " from  
                          " nicht finden."))) 
  (let ((ip (open-input-file from))) 
    (unless (input-port? ip)  
      (error (string-append "Kann die Datei" from  
                            " nicht zum lesen öffnen."))) 
    (let ((op (open-output-file to 'truncate))) 
      (unless (output-port? op) 
        (close-input-port ip) 
        (error (string-append "Kann die Datei " to  
                              " nicht zum Schreiben öffnen"))) 
      (values ip op)))) 
 
(define (my-copy-force from to) 
  (define (print-message) 
    (if (not (= (file-size from) (file-size to)))
        (display (string-append "Kopieren fehlgeschlagen," 
                              " bitte kontrollieren Sie" 
                              from " und " to)) 
        ; optimistisch 
        (display "Kopieren hat funktioniert"))) 
  (call-with-values  
   (lambda () (open-files from to)) 
   (lambda (ip op) 
     ; Input/Output Port ok kopieren 
     ; Achtung! Es wird angenommen das Schreiben und Lesen immer 
     ; glatt geht, das ist nicht garantiert (Platten sollen  
     ; voll werden können ;-) 
     (do ((ch (read-char ip) (read-char ip))) 
       ((eof-object? ch) (begin (close-input-port ip)  
                                (close-output-port op) 
                                (print-message))) 
       (write-char ch op)))))

Wenn der Rückgabewert zählt: for-each

Die Funktionen for-each entspricht in ihrer Funktionsweise map , wird aber hauptsächlich dann eingesetzt, wenn es hauptsächlich auf Seiteneffekte, statt auf den Rückgabewert wie bei map , ankommt. Möchten Sie die begrenzten Ausgabemöglichkeiten von Scheme erweitern, bietet sich for-each an:

(define (write-line args)   (for-each (lambda (arg) (display arg) (display " ")) args)   (newline))

For-each erwartet wie map eine Funktion mit einem Parameter und einer Liste. Auf jedes Element der Liste wird die Funktion angewandt. Im Beispiel gibt die Funktion ein Argument gefolgt von einem Leerzeichen aus. Am Ende der Ausgabe wird zusätzlich eine neue Zeile ausgegeben.

Aufrufen können Sie dies Ausgabemethode mit (writeln `(a b “foo” ,(+ 1 2)) ). Sie erhalten dann a b foo 3 in einer eigenen Zeile.

Nachtrag Nummer 2 Mehrere Rückgabewerte

Wollen Sie mehrere Werte aus einer Funktion zurückgeben, können Sie in Scheme values und call-with-values einsetzen. In C muss man sich dann oft mit verschiedenen “Tricks” behelfen. Entweder verändern Sie übergebene Parameter oder “verpacken” den Rückgabewert in eine Struktur.

(call-with-values (lambda () (values 4 5 6)) +) 
call-with-values

erwartet zwei Funktionen, die eine (der “Produzent”) liefert mehrere Rückgabewerte, die dann in der “Verbraucher”-Funktion bearbeitet werden. Der Produzent hier liefert die Zahlen vier, fünf und sechs zurück, die dann im addiert werden. Daher erhalten wir als Ergebnis 15.

Erweiterungen zu Standardscheme

Alle bisherigen Beispiele sollten Sie in jedem standardkonformen Scheme implementieren können. Darüber hinausgehende Möglichkeiten sind durch Erweiterungen Ihres gewählten Schemes oder Bibliotheken abzudecken. Da DrScheme eingesetzt wurde, liegt es nahe, dessen Erweiterungen genauer zu untersuchen. Als Alternative bietet sich die portable Slib an.

Wenn Sie sich der Dienste der Slib in DrScheme bedienen wollen, müssen sie den folgenden Vorspann laden: @Li: (require-library “init.ss” “slibinit”).

Erweiterte Ein/Ausgabe

Eine verbesserte Kopierfunktion für Dateien Erweiterungen von DrScheme zeigt Listing 5.

Damit nähern wir uns langsam Programmen, die man auch benutzen darf ;-). Unless entspricht einem einseitigen Test ähnlich dem von uns implementierten my-when. Statt (unless (file-exists? .. hätte man auch (when (not (file-exists? … schreiben können, unless ist also die logische Verneinung des when . Mit file-exists? testet man, ob eine Datei vorhanden ist.

Es dürfte müßig sein darauf hinzuweisen, dass auf einem Mehrbenutzersystem ein solcher Befehl nur begrenzt funktionsfähig ist. Der Parameter ‘truncate löscht den Inhalt einer Datei, bevor in sie geschrieben wird; der Parameter ist im Standard zwar nicht vorgesehen, solche Erweiterungen sind aber nicht verboten. Es steht Ihnen selbstverständlich frei, diese Funktion weiter zu verbessern. Wie heißt es dazu so freundlich in Büchern zur Programmierung, “Das sei dem geneigten Leser als Aufgabe belassen”.

Möchten Sie diese Kopierfunktion mit Hilfe der Slib implementieren, müssen Sie entweder unless mit dem passenden if ersetzen, oder die beiden Macros in der Slib definieren, sowie eine Funktion finden, die funktional open- … ‘truncate entspricht.

Listing 7: Einfache Formatierungen

(require-library "init.ss" "slibinit") 
(define (display-with-format character) 
  (require 'format) 
  (let ((code (char->integer character))) 
    (format #t "ASCII Code for ~a is (~S) is: ~%" 
            character character) 
    (format #t "~5TBinär: ~35T~b~%" code) 
    (format #t "~5TOctal: ~35T~o~%" code) 
    (format #t "~5TDezimal: ~35T~d~%" code) 
    (format #t "~5THexadezimal:  ~35T~x~%" code) 
    (format #t "~5Tin English (cardinal): ~35T~R~%" code) 
    (format #t "~5Tin Englisch (ordinal):  ~35T~:R~%" code) 
    (format #t "~5Tin Latein:  ~35T~@R~%" code) 
    (format #t "~5Tin Alt-latein:  ~35T~:@R~%" code) 
    (format #t "~5Tals Zeichen:  ~35T~c~%" character) 
    (format #t "~5Tals Fliesskomma:  ~35T~F~%" code) 
    (values) ; return nothing 
    ))

Die Ausgabe dieser Funktion für #A ergibt:

ASCII Code for A is (#A):  
     Binär:  1000001 
     Octal:  101 
     Dezimal:  65 
     Hexadezimal:  41 
     in English (cardinal): sixty-five 
     in Englisch (ordinal): sixty-fifth 
     in Latein:  LXV 
     in Alt-latein:  LXV 
     als Zeichen:  A 
     als Fliesskomma: 65.0

Listing 8: Formatierung mit printf

(define (print-with-printf) 
  (require 'printf) 
  (let ((n 1.333333333) 
        (str "Ein String")) 
    (printf "%f |%15.2f|%c" n n #newline) 
    (printf "%f |%-15.3f|%c" n n #newline) 
    (printf "%f |%015.4f|%c" n n #newline) 
    (printf "%s |%15s|%c" str str #newline) 
    (printf "%s |%-15s|" str str) 
    (values)))

Formatierte Ausgabe

Die Aufbereitung von Daten spielt bei der Nutzung von Computern eine zentrale Rolle, um die Daten dem Benutzer “ansprechend” darzustellen, benötigt man formatierte Ausgaben. Da ich die DrScheme-Erweiterungen nicht besonders gelungen finde (keine links- oder rechtsbündige Ausgabe, keine Längenbegrenzungen) bevorzuge ich die Funktionen der Slib. In der Datei format.scm wird die Funktion format definiert , deren Möglichkeiten zur formatierten Ausgabe über das printf in C hinausgehen und sich – was für ein Zufall – an die Ausgabemöglichkeiten von Common Lisp anlehnen . Bevorzugen Sie die xprintf Familie, kann Ihnen aber auch damit geholfen werden.

Sie sehen in Listing 7, dass bei format die Tilde die Ersetzungen auslöst. Es gibt eine Reihe weiterer Ersetzungen, die sie bitte in der Dokumenation zu Slib (slib.html#SEC46) nachlesen. Für unsere C-Freunde steht in Listing 8 alternativ die Ausgabe mit Hilfe von printf .

Ich behauptete am Anfang dieses Abschnitts, dass format ungemein flexibel ist. Hier zwei etwas ausgefallenere Beispiele:

(let ((list-with-one-element '(1))    (list-with-more-elements '(1 2 3))    (foo     (lambda (al)       (format #t "Element~:[ of~;s of~] the list ~{ ~S~}~c"               (> (length al) 1) al               #newline)))) (foo list-with-one-element) (foo list-with-more-elements) (values))

Mit ~:[ wird eine Fallunterscheidung eingeleitet. Im Gegensatz zu allen sonstigen Gepflogenheiten ist der erste Teil of~; der Fall, falls die Bedingung des Tests nicht zutrifft!. Wenn also mehr als ein Element in der übergebenen Liste vorhanden ist, wird der zweite Teil s of~ ausgeführt. Damit wäre der erste Teil der format Schablone erklärt. Bleibt noch der Teil ~{ ~S~} , ~S erwartet als Parameter eine Liste, deren einzelne Elemente nacheinander ausgegeben werden. Die Aufrufe der Funktion foo ergeben folgende Ausgabe:

Element of the list 1
Elements of the list 1 2 3

DrScheme offeriert nicht nur call/cc sondern auch weitere Ausnahmebehandlungen. Im Handbuch von MzScheme (Help->Manuals->PLT MzScheme) findet sich eine ausführlichere Dokumentation, auch über die Hierarchie der Ausnahmen, daher beschränke ich mich auf sehr einfache Beispiele.

<b6>
 (define (raise-exception-example n m)   (if (= m 0)     (raise (make-exn:application:divide-by-zero            "Sie sollten nicht durch 0 dividieren"             #f #f))          (/ n m)))

Zwar wird die erhobene Ausnahme schon von DrScheme intern erhoben, jedoch wurde im Beispiel statt einer englischen Meldung eine deutsche ausgegeben. Die Ausgabe von @Li: (raise-exception-example 1 0) lautet einfach “Sie sollten nicht durch 0 dividieren”.

Listing 9: Hash-Tabelle

;;; Hash-Tabelle 
(define my-person  
  (let ((ht (make-hash-table))) 
    (hash-table-put! ht 'vorname "Friedrich") 
    (hash-table-put! ht 'name "Dominicus") 
    (hash-table-put! ht 'job "Programmierer (manchmal)") 
    ht)) 
       
(define (ht-person-name person) 
  (hash-table-get person 'name)) 
 
(define (ht-set-person-name person name) 
  (hash-table-put! person 'name name))

Eigene Ausnahmebehandlungen

Rechnen Sie mit Ausnahmen, wollen Sie normalerweise auch darauf reagieren können. Die Ausnahmebehandlung erfolgt in DrScheme durch das Makro with-handlers.

Beispiel für eine benutzerdefinierte Ausnahmebehandlung:

(define (raise-and-catch n m)
   (let ((my-handler
     (lambda ()
      (display "Ein Benutzer-definierter
               Error Handler")
      (newline)
      (values))))
  (with-handlers
      ((exn:application:divide-by-zero?
     (lambda (exn)
      (my-handler))))
    (raise-exception-example n m))))

With-handlers besteht aus zwei Teilen: Im ersten Teil werden die verschiedenen Ausnahmen sowie die Reaktion darauf aufgezählt. Im Rumpf der Funktion befinden sich eine Reihe von Ausdrücken bei der eine Ausahme auftreten kann, auf die Sie dann reagieren wollen. Der Rumpf entspricht einem try während der erste Teil mit catch oder except verglichen werden kann. Bitte beachten Sie die Wahl der Namen für diese beiden Funktionen: Eine vordefinierte Ausnahme kann man mit make-exn:exception-name erzwingen und um herauszufinden welche Ausnahme vorliegt wird exn:exception-name? benutzt.

Hash-Tabellen

Es wurde Ihnen weiter oben die Assoziationslisten vorgestellt, die man auch als Hash-Tabellen auffassen kann, möchten Sie statt Assoziationslisten Hash-Tabellen einsetzen, können Sie das in DrScheme win in Listing 10 (Datei alist-and-hash-tables.scm ) tun.

Die neuen Funktionen in diesem Beispiel sind diejenigen mit *hash-table* im Namen, die selbsterklärend sein sollten.

Die Benutzerschnittstelle in beiden Beispiel ist gleich, was zeigt, wie wichtig Zugriffsfunktionen sind, mit denen Sie sich alle Möglichkeiten offen lassen, die Implementierung zu ändern.

Zusammenfassung und Vorschau

In diesem und den beiden vorherigen Teilen wurde Ihnen die gesamte Palette der Funktionen aus dem Standard vorgeführt. Sie konnten sich überzeugen, dass Scheme meist genau einen Weg für eine Aufgabe anbietet. Damit ist Scheme nichts für Perl Fans ;-). Die “Basiselemente” sind aber so gut aufeinander abgestimmt, dass Sie mit Scheme alle Programmieraufgaben lösen können. Wenn Sie dazu noch auf die Erweiterungen Ihres Schemes zugreifen oder aber auf die Dienste die Ihnen die Slib bietet, sind Sie im Prinzip für alle Aufgaben gewappnet.

Im nächsten Teil werden zusätzliche Erweiterungen von DrScheme und der Slib vorgestellt. Wir werden sehen, dass DrScheme für das Internet geeignet ist und objektorientierte Erweiterungen kennenlernen.

Zudem gibt es die Möglichkeit, alleinstehende Programme zu erstellen, sowie der Zugriff auf in C geschriebene Module.

Last but not least erwarten die Benutzer heutzutage die Möglichkeit, grafische Benutzeroberfläche zu erstellen, auch dafür ist DrScheme mit MrEd gut gerüstet. Immerhin steht MrEd auf allen Systemen zu Vefügung auf die auch DrScheme portiert wurde. Die Plattformunabhängigkeit ist also kein Privileg von Java ;-) (uwo)

Der Autor

Friedrich Dominicus arbeitet seit Version 0.99pl13 mit Linux, hat fast alle Distributionen schon einmal ausprobiert und ist seit 2 Jahren “Debianer”. Derzeit entwickelt er mit einer Handvoll Leuten – selbstverständlich unter Linux – eine neue Software mit Eiffel, die hier sicher einmal vorgestellt werden wird. Seit Februar 2000 ist er zweifacher Vater und freut sich riesig über jeden Tag mit seinen Töchtern. Seine Hobbies sind Computer, Sport und Lesen.

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Nach oben