Scheme (gesprochen: „skiem“) ist eine funktionale Programmiersprache, ein Dialekt von LISP mit Einflüssen von ALGOL. Scheme ist auch eine Sprache, mit der ich mich wegen des Studiums befassen muss und ich finde sie einfach nur grausam.
Scheme soll einfach zu erlernen sein. Für einfache Programme ist das sicherlich zutreffend, weil man in Scheme auf einfache Weise Variablen und Funktionen deklarieren kann.
Beispiel für eine Variablendeklaration:
(define x 10) ;x=10Beispiel für eine Funktionsdeklaration:
(define (f x) ;f(×)=5*x
(* x 5))So weit ist alles wunderbar und im Prinzip hätte man allein mit so einer Zeile bereits ein Programm erstellt, was auch lauffähig ist. Das Drumherum von imperativen Programmiersprachen kann man sich also sparen. Aber wehe dem, der versucht, größere Programme in Scheme zu schreiben (siehe Punkt 5).
Scheme ist in dieser Hinsicht ähnlich wie PHP. Man kann also alles benutzen und muss sich nicht darum kümmern, ob eine Variable nun ein Integer, ein String, eine Funktion oder sonstwas ist. Der Scheme-Interpreter führt Programme mit unsinnigen Typzuweisungen (z.B. wenn man einen String an eine Funktion übergibt, die einen Integer erwartet) anstandslos aus, bricht aber während des Programmablaufs mit einem Laufzeitfehler ab, sobald die fehlerhafte Stelle erreicht ist.
Typfreie Sprachen sind auf den ersten Blick sehr praktisch (weniger Schreibarbeit, mehr Flexibilität, einfach unkomplizierter), aber das führt eben auch schnell zu Laufzeitfehlern. Eine typstrenge Sprache (z.B. Java) würde bei so einem Unsinn von vornherein die Compilierung und den Programmstart verweigern.
An Scheme (und auch an PHP) lässt sich also monieren, dass Seiteneffekte und fehlerhafte Termbildungen/Zuweisungen/Operationen gefördert werden. Zudem werden größere Programme „unsauber“ und unübersichtlicher, denn am Ende weiß kein Mensch mehr, ob die Variable, die er da gerade benutzt, ein String oder ein Integer ist.
Scheme ist eine funktionale Programmiersprache (mal abgesehen davon, dass es eingeschränkte Möglichkeiten zur imperativen Programmierung gibt). Scheme basiert auf Lisp und Lisp basiert auf dem Lambda-Kalkül und das Lamba-Kalkül sagt aus, dass jeder Term eine Funktion bezeichnet. Die Konsequenz daraus ist, dass man in Scheme praktisch immer nur einen Befehl ausführen kann (eine ganz schöne Umstellung, wenn man nur imperative Programmierung gewohnt ist). Genauso wie bei mathematischen Funktionen darf man zwar mehrere Eingabeparameter übergeben, aber es darf am Ende auch immer nur ein Ergebnis rauskommen. Ich kann z.B. innerhalb einer Funktion nicht zwei Rechenschritte machen, wie z.B.
(define (f x) ;f(×)=(10*(5*x)
(x = (* x 5)) ;FALSCH
(* x 10)So ist es richtig:
(define (f x) ;f(×)=(10*(5*x)
(* 10 (* x 5)))Man kann sich mit let und let* behelfen, aber das ist umständlich und nicht direkt mit den Möglichkeiten imperativer Programmiersprachen vergleichbar. Außerdem gibt es ja auch noch rudimentäre Möglichkeiten, imperativ zu programmieren (mit begin), aber das ist böse (weil es eben das funktionale Prinzip von Scheme aufhebt) und daher im Studium eher unerwünscht.
Außerdem sind diese Möglichkeiten alle Syntatic sugar. Syntactic sugar sind Syntaxerweiterungen, also z.B. Befehle, die der Schreibvereinfachung dienen oder wie in Scheme Befehle, die aus imperativen Programmiersprachen übernommen wurden, aber eigentlich nicht „originales Scheme“ sind und sozusagen in Scheme-Befehle konvertiert werden müssen. Es gibt z.B. den while-Befehl in Scheme, um while-Schleifen zu basteln, aber intern macht Scheme daraus eine Rekursion.
Aus Punkt 3 folgt auch, dass man häufig rekursive Algorithmen anwenden muss. Das ist nicht unbedingt schlecht, aber der menschliche Geist ist diese Denkweise ungewohnt und daher muss man sich erstmal darauf einstellen. Dinge, die man in imperativen Sprachen 0815 iterativ programmieren könnte, wirken in Scheme daher unnötig verkompliziert.
Scheme-Programme werden ab einem gewissen Punkt unheimlich unübersichtlich. Also was die Übersicht angeht, könnte selbst Assembler noch dagegen konkurrieren.
Kostprobe:
(define fehlermeldung-keine-liste "Abbruch. Es werden nur Listen als Eingabeparameter akzeptiert.")
(define fehlermeldung-keine-reelle-zahl "Abbruch. Es werden nur Listen mit reellen Zahlen akzeptiert.")
(define add
(lambda (A B)
(cond ((and (list? A) (list? B))
(cond ((and (not (null? A)) (not (null? B)))
(cond ((and (not (pair? (car A))) (not (pair? (car B))) (real? (car A)) (real? (car B)))
(cond ((equal? A '()) B)
((equal? B '()) A)
(else (cons (+ (car A ) (car B)) (add (cdr A) (cdr B)))) ))
(else fehlermeldung-keine-reelle-zahl)))
(else '())))
(else fehlermeldung-keine-liste)))
)Dieses Programm addiert 2 Vektoren (in diesem Fall Listen) miteinander. Das Programm macht nicht so besonders viel, ist aber nicht auf den ersten Blick zu enträtseln.
Scheme hat das Klammernsetzen zu einer Sportart entwickelt. Das obige Beispiel besteht aus 49 Klammern (bzw. Klammerpaaren) und jede davon muss richtig gesetzt sein, sonst läuft der Mist nicht. Meiner Meinung nach verbergen die Klammern den Blick auf das Wesentliche, denn häufig verwendet man mehr Zeit mit der Suche nach falschen oder fehlenden Klammern, als mit der Realisierung des eigentlichen Programms.
Selbst 5-Zeiler können wegen der Klammern und der Unübersichtlichkeit von Scheme zur Tortur werden. Die Suche nach Fehlern ist vergleichbar mit der Suche nach der Nadel im Heuhaufen. Wir haben im Studium ein knappes halbes Jahr mit Scheme rumgemacht und sind jetzt an dem Punkt angelangt, wo wir Vektoren miteinander addieren, multiplizieren etc. können. Gegen Ende des Semesters sind wir auch mit Java angefangen und haben praktisch in den ersten Aufgaben gleich die Java-Version unseres Vektorprogramms erstellt. Das, wofür wir in Scheme fast ein halbes Jahr gebraucht haben, haben wir in Java 2 Wochen benötigt. Darüber kann man einmal nachdenken…
Ich frage mich also, wer sich freiwillig antut, ein „richtiges“ Programm in Scheme zu programmieren. Das ist doch sadomachistisch.
Ganz ehrlich: Ich weiß es nicht. Scheme wird häufig wegen seiner Ausdrucksstärke, seiner (angeblichen) Einfachheit und den höheren Funktionen über den grünen Klee gelobt. Wirklich überzeugt bin ich davon aber nicht, denn das alles kann man auch mit imperativen Programmiersprachen realisieren und die Implementierungen sind in Scheme genauso fehleranfällig, wie in anderen Sprachen.
Eigentlich kann man mit Scheme auch nicht so besonders viel machen, außer mathematische Funktionen auszuwerten.
Scheme-Programme werden in der Regel von einem Interpreter zum Leben erweckt. Interpreter sind langsamer, als kompilierte Programme. Ergo: Scheme ist nicht mal besonders schnell.
Ok, das ist nicht ganz richtig. Ich habe auf meinem Rechner Scheme-Code in GnuCash, Gimp und vielleicht eine Handvoll kleinerer Bibliotheken entdeckt. Aber auf jeden Fall ist Popularität kein schlagendes Argument, sich Scheme anzutun.
Der normale Mensch hat in seinem Leben wohl (aus gutem Grund) nur mit imperativen Programmiersprachen zu tun. Scheme ist deshalb ganz interessant, weil man gezwungen wird, auch mal über den Tellerrand zu gucken. Das entschuldigt natürlich nicht, dass Scheme einfach ekelhaft ist und einem die letzten Nerven rauben kann.
Hab mir gerade noch mal deine Vectoraddition angesehen. Wozu die Überprüfung real? Soll die Addition für Integer nicht funktionieren?
Hier ist meine Version:
(define (add A B)
(cond
((or (not (list? A)) (not (list? B)))
fehlermeldung-keine-liste)
((or (null? A) (null? B))
’())
(else (cons
(+ (car A) (car B))
(add (cdr A) (cdr B))))))
Zu dem rekursiven PHP-Scheme-Beispiel: Der Unterschied zeigt sich nicht bei fak(5), jedoch schon bei fak(50), wo PHP eine E+064-Zahl liefert, bei fak(500) lediglich ein 1.#INF und bei fak(50000) eine ’unknown software exeption’, während Scheme mit (fak 50000) nach einer bestimmten Zeit und vor allem mit konstantem Speicher ein Ergebnis liefert.
tail-call wäre eine Antwort zu 8., was Scheme eigentlich kann bzw. hat. Ein paar Links zu diesem Thema habe ich unter http://blog.choas.net/TailCall liegen.
Leider stempelst Du Sheme und wie ich es heraushöre auch die funktionale Programmierung als ekelhaft und nicht gerade sehr leistungsfähig ab. Dem kann ich leider gar nicht zustimmen, auch wenn ich Dich ein bisschen verstehen kann, denn Sheme ist nicht gerade die funktionale Sprache, mit der ich als Professor dem lernwilligen Stundenten die funktionale Programmierung nahelegen würde.
Haskell oder ML scheinen mir da weitaus mehr geeignet, im Gegensatz zu Sheme lernt man mit diesen Sprachen auch die Mächtigkeit der funktionalen Programmierung kennen und sieht, dass in ein oder zwei Dekaden die funktionale Programmierung das imperative Pendant abgelöst haben wird, denn auch der durchschnittliche Programmierer wird bis dahin so fit sein müssen, dass die Komplexität funktionaler Programmierung kein Hindernis mehr für ihn ist. Auch mir wurde im ersten Semester nahegelegt, mich mit Sheme zu beschäftigen, aber nur als Ergänzung zu anderen, wie oben erwähnt weitaus mächtigeren, funktionalen Sprachen
Mich würde interessieren welche Literatur Dir empfohlen wurde, dass Du solch eine Meinung vertrittst und die funktionale Programmierung als notwendiges Übel abtust. (Ich bin übrigens mit C++ und Java aufgewachsen, habe aber durch das (Selbst-)Studium erfahren, dass man gerade mit funktionalen Sprachen sehr viel mehr zu leisten im Stande ist, als mit ebendiesen Sprachen)
Lernen ist schwer - deswegen und weil es mir früher ebenso ergangen ist, kann ich Deine Aufregung verstehen.
Nichts destotrotz sind fast alle Deine Argumente FALSCH. Zum Beispiel kann Scheme (im Sinne von die meisten Scheme-Implementierungen) alle objekt-orientierten Programmiertechniken auch Mixins oder auch mehrere Funktionswerte zurückgeben. Nicht zuletzt könntest du solche auch in eine Liste packen und diese dann zurückgeben. "Den" Lambda-Kalkül gibt es nicht. Aber alle Lambda-Kalküle, die ich kenne, können Funktionen auf Funktionen abbilden. Dann: das Modulsysteme der meisten Scheme-Implementierungen übertreffen den Funktionsumfang von z.B Java oder C bei weitem. Usw. usf.
Eine etwas übersichtlichere Implementierung Deines Beispiels sieht beispielsweise so aus:
(define (add ls1 ls2)
(map (lambda (a b)
(+ a b))
ls1
ls2))
(define (add ls1 ls2)
(map (lambda (ls) (apply + ls))
(map list ls1 ls2)))
(add ’(1 2 3 4) ’(1 2 3 4))
Ein Dreizeiler also (höchstens) - komplett ohne Fehlerbehandlungscode (macht alles Scheme), polymorph, anwendbar auf alle Datentypen, für die "+" existiert und sinnvoll ist und LESBAR.
Fang’ doch endlich an, Dich mal darauf einzulassen!
Das ist wohl die berüchtigte Ausdrucksstärke von Scheme.
Uns wurde immer gesagt, dass man einen Algorithmus einfach in Prosa formulieren und ihn dann nur noch 1:1 als Scheme-Programm reinhacken muss. Dazu kommt nur noch die Abbruchbedingung und das Programm ist fertig.
Ganz so profan finde ich diesen Vorgang eigentlich nicht und ich finde imperative Programmiersprachen da auch nicht unbedingt im Nachteil. Eine Rekursion lässt sich bei den meisten imperativen Programmiersprachen doch auch recht einfach formulieren. Das absolute Paradebeispiel ist da ja die Fakultätsfunktion.
In Scheme:
(define (fak x)
(if (= x 1) 1
(* x (fak (- x 1)))))
(fak 5)
In PHP:
<?php
function fak($x) {
if ($x==1) return 1;
else return ($x * fak($x-1));
}
echo fak(5);
?>Das Ergebnis von 5! ist 120.
Scheme und PHP unterscheiden sich hier bis auf das „Drumherum“ von PHP nicht besonders, oder?
War grad ein wenig auf Recherche im Netz als ich deinen Artikel las. Ich stimm dir prinzipiell zu, dass imperative Sprachen intuitiver sind als funktionale oder logische. Aber auch die letzten beiden haben ihre Daseinsberechtigung. Rekursive Programme sind mit ihnen wesentlich schneller implementiert als in Java/C. XSLT ist z.B. ein prominentes Beispiel einer funktionalen Sprache zur Verarbeitung von XML-konformen Daten. Wer sich schon mal mit einer imperativen Sprache mit DOM-Manipulation rumärgern durfte, dem sei XSLT wärmstens ans Herz gelegt.
just my 2 cents
Auch ich musste in meinem Studium Scheme lernen - und vielleicht habe ich einfach einen besseren Professor gehabt, aber ich kann so gut wie keine Deiner Argumente nachvollziehen.
Ich muss aber auch gestehen, dass ich zuvor schon einmal mit Haskell programmieren durfte (auf dem Gymnasium), daher war mir das Konzept jetzt nicht ganz so neu.
Aber zum Punkt "Wofür man Scheme braucht":
Ich denke Scheme wird vor allem dadurch immer wichtiger, dass wir uns von serieller Verarbeitung fort bewegen und immer weiter zur parallelen Verarbeitung übergehen - hier gibt es ein Problem:
- Zustände.
Probleme die mit Threads, Multithreading, Thread Safety, etc. gelöst werden, gibt es in der funktionalen Programmierung nicht, im Gegenteil. Da es keine Zustände gibt, erlaubt erst die funktionale Programmierung echte Parallelisierung (Multithreading ist ja nur simulierte Paralellisierung von Prozessen). Erst hiermit können Entwicklungen, wie die Leistung von Multi-Kern-Prozessoren vollständig entfaltet und genutzt werden.
Abgesehen davon gibt es einige weitere Vorteile. Auf dem Gymnasium durften wir uns mit dem Knacken monoalphabetisch verschlüsselter Texte beschäftigen. Zählen von Buchstabenhäufigkeiten, übertragen auf die Häufigkeit natürlicher Texte, Entschlüsseln, Prüfen auf Plausibilität der Entschlüsselung - das war umgesetzt in Funktionaler Programmierung eine Hand voll "Dreizeiler" (durch Abarbeiten der Informationen als Listen mithilfe von Rekursion) - die Stufe unter uns hat das gleiche Projekt gehabt, aber in Java - der Umfang der Programme war um ein vielfaches größer.
Natürlich hat funktionale Programmierung auch ihre Kehrseiten, aber da ist es ähnlich wie bei vielen anderen Programmiersprachen auch: Man muss Problemabhängig entscheiden, welche Sprache und welches Paradigma hierfür idealer Weise eingesetzt wird. Genauso wie Java und Objektorientierung ungeeignet ist für die Treiberprogrammierung und niemand auf die Idee käme, ein ERP-System in Assembler zu schreiben, gibt es auch Beispiele, in denen funktionale Programmierung die beste Wahl ist - und andere Fälle, wo man lieber Java, o.Ä. wählt.
Und zum Thema interpretierte Sprachen: Deine Referenzen, Java und auch PHP, sind ebenfalls teilinterpretiert. Viele andere Sprachen bedienen sich ebenfalls dieses Vorteils, da es die Programme plattformunabhängig macht. Gerade bei dem wachsenden Hardwarekapazitäten, welche die Nachteile von interpretierten Sprachen kompensieren wird deren Popularität bestimmt weiter Wachsen.
Zu den anderen Punkte wurde eigentlich schon genug gesagt ;-)
Viele Grüße,
pygospa
Dass Scheme nicht kompiliert wird ist falsch. Schau dir mal Gambit oder Chicken Scheme an.
Funktionale Programmierung wird immer wichtiger und diese Konzepte werden sehr erfolgreich eingesetzt. Erlang ist eine funktionale Programmiersprache die 20% den amerikanischen Telefonverkehrs managt. Google hat MapReduce entwickelt, das zwar C++ ist, aber funktionale Konzepte (eben map und reduce) implementiert.
Ein paar Sachen sind wohl einfach Geschmackssache. Ich mag die einfache Syntax, sprich die Klammern und ich mag die Vereinfachung durch Typfreiheit. Aber ich muss Robert zustimmen - Haskell ist vermutlich geeigneter um funktionales Programmieren zu lernen.
Zwei Listen addieren:
(define (add ls1 ls2) (map + ls1 ls2))
Das ist alles.
Für Scheme gibt es viele Compiler.
Menü
Beitragsübersicht
Kontakt & Impressum
Über mich
Linkempfehlungen/Linkpartner
Letzte Kommentare
»UTF-8 und die Entity…
»Validierung, wozu? Waru…
»Backlinks gegen Content…
»Gute Weiterleitung, bös…
»Essay zu Quizsendungen/…
Meta
RSS 2.0
XHTML-Validierung
CSS-Validierung
Backlinks
»Spam vermeiden
»Internetagentur Ulm
»Bruststraffung
»Achterbahn & Freizeitpark
»Fernstudium
»TuS Graf Kobbo Tecklenburg