Posted by Patrick Krusenotto on Mär 21, 2009 in
Allgemein,
Linux
Ich habe mir ein Notebook Acer 5630Z gekauft. Ein schönes Notebook, das im T-Online-Shop ohne Betriebsystem angeboten wird. Ich konnte Kubuntu 8.04 ohne Probleme installieren. Leider habe ich es nicht geschafft, das WLAN ans Laufen zu bekommen.
Daraufhin habe ich Ubuntu 8.10 installiert. Es lief ebenfalls auf Anhieb. Um das WLAN ans Laufen zu bringen, reicht es, diesen Treiber zu installieren. Dabei muss der Rechner eine Kabelverbindung ins Internet haben, da der Paketmanager weitere Pakete installieren will. Danach muss der Rechner neu gebootet werden. Dann arbeitet auch das WLAN.
Ob das auch unter Kubuntu 8.10 funktioniert, weiss ich nicht. Ich nehme aber an, daß es ebenfalls Problemlos arbeitet ich habe es nicht ausprobiert.
Tags: 5630Z Linux WLAN
Posted by Patrick Krusenotto on Okt 10, 2008 in
Algorithmen,
Allgemein,
Perl

Wenn sie diesen Artikel verstehen möchten, muss Ihnen der Begriff der “Closure” geläufig sein. Einen Artikel dazu werde ich der Vollständigkeit halber später noch verfassen.
Eine anonyme…
Wie in einigen anderen Programmiersprachen können in Perl Funktionen anonym definiert werden. Das bedeutet, sie werden an keinen Namen gebunden, sondern sofort aufgerufen, einer Variablen zugewiesen oder weitergereicht.
print sub{$_[0]*3}->(8);
gibt zum Beispiel 3*8 = 24 auf die Konsole. Die Sub wird “on the fly” definiert. und mit “….->;(8)” direkt aufgerufen. Danach wird sie allerdingds ein Opfer des Garbage Collectors. Es erfolgt keine Bindung an einen Namen, wie das bei
sub mal3 {$_[0]*3}
der Fall wäre.
Will man die Tatsache, daß eine “ordentliche” Funktionsdefinition nur die Bindung einer anonymen Funktion an einen Bezeichner ist, in der Notation expliziter machen, kann man in Perl auch folgendermaßen schreiben:
*mal3 = sub{$_[0]*3};
Das Ergebnis ist völlig identisch.
Um eine Funktion zu definieren, kann man also auf einen Namen verzichten. Namen sind eben Schall und Rauch… Doch halt! Was macht man, wenn man eine Funktion rekursiv definieren möchte? Wie formuliert man den rekursiven Aufruf, wenn das Kind keinen Namen hat? Gibt es da ein eigenes Sprachmittel, etwa so wie den Bezeichner “SUPER” , mit dem man in einer Methode auf die unmittelbare Elternklasse kommt, ohne zu wissen, wie sie heisst?
… und trotzdem rekursive Funktion …
Antwort: Nö. Aber: Es gibt aber eine Lösung. Sie kommt allerdings aus einer ganz anderen Ecke. DIe Lösung trägt den Namen Y-Kombinator. Diesen wollen wir jetzt ausprobieren.
Wir nehmen uns zunächst ein ödes Beispiel einer rekursiven Funktion daher: Die Fakultät. Nach dem oben Gesagten können wir das in Perl wie folgt schreiben:
*fak =
sub {
my $n=shift;
if ($n==0) {
return 1
}
else {
return $n * fak($n -1);
}
};
Was an dieser Definition nun stört, ist das Auftreten des Namens fak in der siebten Zeile. Um ihn loszuwerden, müssen wir die rechte Seite der Zuweisung, “*fak=..”, also die anonyme Funktion nach fak abstrahieren. Wir verpacken sie daher in einen Parameter $psi und geben nun nicht mehr den Wert der Berechnung, sondern eine neue Funktion als Berechnungsergebnis zurück.
Das neue Gebilde ist die nicht-rekursive Funktion pfak (”parameterized fak”):
*pfak =
sub {
my $psi=shift;
return sub {
my $n=shift;
if ($n==0) {
return 1
}
else {
return $n * $psi->($n -1);
}
}
};
Das ist eine Funktion, die als Wert eine (anonyme) Funktion zurückgibt. Eine Funktion, die das tut, heißt in der Mathematik auch “Funktional”.
Wenn wir also an pfak die Funktion fak übergeben, erhalten wir die Funktion fak. Dazu muss man sich nur überlegen, daß dadurch der Parameter $psi mit der Funkion fak belegt wird. Wir könnten also fak nun folgendermaßen umdefineren, ohne die Funktion damit zu verändern:
*fak = pfak(\&fak)
Diese Gleichung besagt: efak bildet die Funktion fak auf sich selbst ab. (Kein Wunder, denn dafür haben wir sie ja auch gebaut!). Soetwas heißt in der Mathematik “Fixpunktgleichung”. Der Fixpunkt ist die Funktion fak. Das ist nichts anderes als eine Feststellung wie 1² = 1. Die Zahl 1 ist ein Fixpunkt der Quadratfunktion.
Tun wir jetzt einmal so, als wüssten wir über fak garnichts. Darum nenne ich fak jetzt hurz.
hurz === pfak(\&hurz)
(Der Operator “===” soll die Gleichheit zweier Funkionen andeuten)
Alles, was wir über hurz wissen, ist, daß die Funktion ein/der Fixpunkt von pfak ist. Falls es möglich ist, hurz aus pfak zu bestimmen, dann gäbe es ein Funktion Y, sodaß gilt:
hurz === Y(\&pfak)
… dank des Y-Kombinators!
Ja, also was soll man sagen? Eine solche Funktion gibt es! Es ist der Y-Kombinator von Haskell Curry, den dieser in den 40er Jahren gefunden hat. In Perl hat er folgende Gestalt:
sub Y {
my $e=shift;
sub {
my $x=shift;
sub {
my $u=shift;
($e->($x->($x)))->($u)
}
}->(sub {
my $x=shift;
sub {
my $u=shift;
($e->($x->($x)))->($u)
}
}
)
}
Wir stellen zunächst fest, daß der Y-Kombinator nicht rekursiv ist. Es findet kein Aufruf von Y innerhalb von Y statt. Daß Y die Funktion fak als Fixpunkt von pfak “findet”, können Sie ganz leicht feststellen, indem Sie jetzt Ihren Perl-Interpreter anwerfen und die Funktionen pfak und Y hineinwerfen. Danach können Sie sich zum Beispiel mit
print Y(\&pfak)->(10)
die Fakultät der Zahl 10 berechnen lassen.
Daß das Ganze funktioniert, kann man ausprobieren, aber es ist alles Andere als einleuchtend. Darum müssen wir den Y-Kombinator etwas genauer unter die Lupe nehmen. Es bleibt nämlich die unbewiesene Behauptung, daß gilt:
fak == Y(\&pfak)
beziehungsweise, fak oder eben auch Y(\&pfak) Fixpunkt von pfak ist. Zu diesem Zweck kann man sich einfach mal anschauen, was passiert, wenn Y mit dem Parameter &pfak aufgerufen wird, indem jedes Vorkommen von $e-> durch efak ersetzt wird:
Y(\&efak) ===
sub {
my $x=shift;
sub {
my $u=shift;
(pfak($x->($x)))->($u)
}
}->(sub {
my $x=shift;
sub {
my $u=shift;
(pfak($x->($x)))->($u)
}
}
)
Was hier entstanden ist, besteht offenbar aus zwei gleichen Teilen: Es ist eine sub, die sich selbst übergeben wird. Es ist also ein Ausdruck der Form $A->($A), wenn man annimmt, daß
$A = sub {
my $x=shift;
sub {
my $u=shift;
(pfak($x->($x)))->($u)
}
};
gilt.
Rechnet man weiter, was das bedeutet, indem $A in $A->() eingesetzt wird, ergibt sich
$A->($A)===sub {
my $u=shift;
(pfak($A->($A)))->($u)
}
Man kann bereits erkennen, wo die Reise hingeht. Rufen wir zurück, daß $A->($A) genau Y(\&pfak) ist,
Lesen wir
Y(\&pfak) === sub {
$u=shift;
(pfak(Y(\&pfak))->($u)
}
Und damit gilt für Zahlen $N:
Y(\&pfak)->($N) === pfak(Y(\&pfak))->($N)
Was nichts anderes besagt, als daß Y(\&pfak) Fixpunkt von von pfak ist, also die Lösung unserer ursprünglichen Fixpunktgleichung.
Das ist eben Perl !
Zugegeben: Die Materie ist nicht ganz einfach. Aber es lohnt sich, über den Y-Kombinator nachzudenken und damit zu spielen. Er zeigt auch eindrucksvoll, wie korrekt und vollständig das Design des Perl-Laufzeitsystems ist. Java zum Beispiel kann den Y-Kombinator nicht darstellen, denn dort sind Funktionen (leider auch Klassen) keine First-Class-Objects. Weiterhin kennt Java keine Closures, die hier aber dringend erforderlich sind.
Posted by Patrick Krusenotto on Okt 3, 2008 in
Algorithmen,
Allgemein,
Perl

Scanner
Mit Perl einen Scanner zu schreiben, ist eine einfache Sache, denn Perl hat eine sehr potente Implementierung für reguläre Ausdrücke. Allerdings muss man wissen, welche Modifier erforderlich sind, damit der Lexer auch immer an der Stelle weitermacht, wo er aufgehört hat. Dazu benutzt man den Pattern Matcher mit dem Modifier /g und startet jedes Matching mit dem Anker \G. Durch /g wird erreicht, daß Perl sich die aktuelle Scanposition merkt und mit \G verlangen wir, das bei der nächsten Mustererkennung genau an dieser Stelle weitergemacht werden soll.
Zusätzlich muss der Modifier /c angegeben werden, damit Perl die Position auch dann behält, wenn ein matching-Versuch scheitert und deswegen ein anderes Pattern versucht werden soll (was der häufigste Fall sein wird). Die weiteren Modifier, die verwendet werden sind /s , damit der Scanner mit einem Text umgeht, als wäre es eine einzelne Zeile und manchmal /i, damit “case-insensitiv” gescannt wird.
Von einem Lexer erwarten wir, daß er einen String in Tokens zerlegt und bei jedem Aufruf das nächste erkannte Token zurückgibt. Außerdem brauchen wir von jedem Token das gescannte Lexem. Bei jedem Aufruf bekommen wir also zwei Werte. Wird zum beispiel des Schlüsselwort “select” erkannt, bekommen wir das Wertepaar ['KEYW' , 'select'] geliefert.
Hier also die Implemetierung eines Fragmentes eines SQL-Scanners Das Lexen übernimmt die sub lex. Darunter eine Schleife, die die Ergebnisse ausgibt.
use strict;
$_ = <<EOT;
select name,alter
FROM person
where name like \'Kaz%mir\' and alter>20;
EOT
sub lex {
/\G(\s+)/gcs; # spaces wegfuttern.
return [$1,'OP'] if /\G"([\*\+\-\/]*)"/gcs;
return [uc($1),'KEYW'] if /\G(from|like|select|where)/gcsi;
return [$1,'COMM'] if /\G(\,)/gcs;
return [$1,'SEMI'] if /\G(\;)/gcs;
return [$1,'COMP'] if /\G(>|<|>=|<=|=)/gcs;
return [$1,'STRI'] if /\G'([^']*)'/gcs;
return [$1,'IDEN'] if /\G([a-zA-Z_]+)/gcs;
return [$1,'INTE'] if /\G(\d+)/gcs;
return undef if /\G$/;
die "lexer error at '",substr(substr($_,pos($_)),0,60),"'";
}
while (my $t = lex) {
print "$t->[1] '$t->[0]' ",pos(),"\n";
}
Das hier vorgestellte Vorgehen ist einfach zu überblicken und der Scanner arbeitet schnell. Wie man einen einfachen Recursive-Descent-Parser dazu baut, werde ich eventuell ebenfalls einmal hier notieren.
Tags: Flex, Lexer, Perl, Scanner
Posted by Patrick Krusenotto on Okt 1, 2008 in
Algorithmen,
Allgemein,
Perl
Datenbanken kennen Aggregatfunktionen. Ein Aggregat ist - Ja? - leider daneben! Ein Aggregat ist eine Anhäufung. Im Grunde ist die ehrfurchtsgebietende Miene, mit der ein Heizungsfachman ein elektronisches Kästchen aus einer Packung holt, während er dieses als Aggregat bezeichnet um es darauf in die kaputte Heizung einzubauen, eher lachhaft. Würde er sagen, er baue jetzt einen elektronischen Haufen in die Heizung ein, würden wir eher grinsen als ehrfürchtig Abstand haltend sein fachmännisches Tun zu würdigen.
Mit Aggregat ist also gemeint, dass wir etwas unübersichtiches zu einem Haufen zusammenwerfen, um den Überblick zu behalten. Etwas vornehmer nennt man diesen Vorgang manchmal auch Faltung.
Beispiele bei Datenbanken sind zum Beispiel COUNT(*) für die Anzahl der Elemente, AVG(*) für deren Durchschnitt und SUM(*) die Summe aller Elemente.
Was mich immer schon geärgert hat ist, ist, das zumindest bei Datenbankten kein offensichtlicher Weg existiert, solche “Aggregatfunktionen” selber zu definieren. Mag sein, daß einzelne dieser Datenbanken solche Möglichkeiten haben. In der SQL’92 Spezifikation sind Aggregatfunktionen vorgegbene schwarze Kisten, die man benutzen kann. Sonst nichts.
Das Wesen einer Aggregatfunktion oder Faltung ist, eine Liste “zusammenzufalten”, sodaß man Ende der Operation ein einzelnes etwas (etwa eine Summe) anstatt einer unhandlichen Liste hat.
Wie kommt man zu einer Aggregatfunkion? Aus der Operation z(a,b)=a+b gelang man zu der Aggregatfunktion SUM(*) aus der Operation z(n,a)=n+1 gelangt man zu Aggregatfunktion COUNT(*) und anderes mehr. Jeder Programmieranfänger hat schonmal eine Aggregation programmiert, ohne sich darüber Gedanken gemacht zu haben. Dazu ist der Vorgang auch insgesamt zu banal. Zumeist wird man wohl eine FOR-Schleife verwendet haben. Das ist sehr schön. aber nicht unser Thema. gesucht ist eine Generalisierung dieses Vorgangs. Das bedeutet, daß wir die gesamte Faltung von dem konkreten Faltungsoperator abstrahieren müssen.
Angenommen, wir haben eine Liste L=(x1,x2,..xn) sowie eine zweistellige Faltungsoperation z. Dann ist die Faltung f eine Funktion höherer Ordnung mit dem Faltungsoperator z und der Liste L als Argumenten:
f(z,L) = z( L[0] , L[1] ) falls die Liste zweielementig ist und
z( L[0] , f(z, L[1..n]) sonst
Beispiel:
z(a,b)=a+b und L=[4,3,2,6]
Dann ist
f(z,L)=
4+f(z,[3,2,6])=
4+3+f(z,[2,6])=
4+3+2+6=15.
Schönheitsfehler ist noch, daß unsere Konstruktion nur mit mindestens zweielementigen Listen arbeitet. Dem kann man mit einem zusätzlichen Startparameter s begegnen, mit dem das erste Listenelement verknüpft wird.
Dann kommt man zu folgender Version:
f(s,z,L) = s falls die Liste leer ist,
z( L[0] , f(s,z, L[1..n]) sonst.
In Perl kann das so aussehen:
sub foldleft ($&@) {
my ($s,$z,@l)=@_;
return $s unless @l;
my $a = shift @l;
return $z->($a,foldleft($s,$z,@l));
}
Schöner und schneller ist aber diese iterative Version:
sub foldl ($&@) {
my ($s,$z,@l) = @_;
$s = $z->($s,$_) for (@l);
return $s
}
Wozu das gut ist? Ganz einfach: Durch Konstruktionen wie diese spart man innere Schleifen. Um nun die Summe aller Elemente einer Liste zu bilden, kann man
$summe = foldl 0, sub {$_[0]+$_[1]}, @LISTE
schreiben.
Um alle Zeilen einer Datei zusammenzufassen und dabei zwischen je zwei Zeilen eine Leerzeile einzubauen:
$text = foldl 0, sub {$_[0] . "\n". $_[1]}, <FILE>;
Anstatt hier fehleranfällig mit Schleifen und Variablen zu arbeiten, kann man eine einzige Funktion aufrufen. Funktionale Programmiersprachen wie Haskell und andere haben eine solche Faltungsfunktion direkt mit eingebaut. Interessant ist auch, daß große Teile der Programmentwicklung sich gerade mit Aggregationen befassen, auch wenn uns das nicht immer bewusst ist. Wer als Programmierer ein bisschen darüber nachdenkt, was er gerade tut, stößt täglich selbst auf Beispiele. Jeder Erstellung einer HTML-Seite aus einer Datenbankabfrage ist ein solches Beispiel.
Tags: Aggregation, Faltung, Fold, Perl
Posted by Patrick Krusenotto on Jun 5, 2008 in
Algorithmen,
Allgemein

Für ein aktuelles Projekt benötge ich einen Parsebaum aus XML-Dokumenten, der aus Objekten besteht. Ich bin sicher, daß schon mal so oder ähnlich jemand gemacht hat. Leider ist CPAN mittlerweile recht groß…
Meine Vorstellung ist, daß der Tagname der Klassenname des Objektes wird. Zu diesem Zweck muss der etwas gewöhnungsbedürftige Output von XML::Parser transformiert werden. Die Objekte sollen Hashes sein mit den Einträgen “attr” und “data”. “attr” soll seinerseits ein hash der Attributwerte sein und data ein array mit den Sub-Objekten.
Los gehts:
package main;
use XML::Parser;
use Data::Dumper;
my $p = new XML::Parser(Style => 'Tree');
my $t= $p->parse('<html htmlattr1="xx">
<head headattr1="a" headattr2="b"><title>test</title></head>
<body>
<div style="color:red">murx</div>
</body>
</html>');
print Dumper($t);
sub transform {
my ($tag,$content) = @_;
return $content unless $tag;
my @content = @$content; #kopie!
my $attr = shift @content;
my %attr = %$attr; #kopie!
# an dieser stelle haben wir mit $tag, %attr und @content eine
# repräsentation des aktuellen tags
my @tags;
while (@content) {
my $t = shift @content;
my $c = shift @content;
push @tags, transform($t,$c);
}
my $obj={
attr => \%attr,
data => \@tags,
};
return bless $obj,$tag;
}
my $objtree = transform(@$t->[0], $t->[1]);
print Dumper($objtree);
Tags: parsing, Perl, XML
Posted by Patrick Krusenotto on Apr 2, 2008 in
Allgemein

Ein Backup von einem 1und1-host bereitet mir schon seit Monaten immer wieder mal Kopfzerbrechen. Grund ist, daß diese Präsenz nur via ftp und sftp zugreifbar ist, nicht aber mit ssh oder scp. Da ftp aus naheliegenden Gründen nicht infrage kam, führte der Weg erstaunlicher Weise über den ssh-client sshfs. Erstaunlich deswegen, weil ich bisher davon ausging, daß hierzu der Zugriff über scp erforderlich ist. Dieser ist aber vom Anbieter gesperrt.
sshfs ist offenbar in der Lage, ein entferntes Verzeichnis via sftp zu mounten. Sehr schön!
Vorgehensweise war nun diese:
0. sshfs local installieren
sudo aptitude install sshfs
1. mount-point einrichten
sudo mkdir -p /mnt/www/beispiel.de
2. Entferntes Verzeichnis read-only mounten
sudo sshfs -o ro benutzername@beispiel.de: /mnt/www/beispiel.de/ -o allow_other
3. Daten mit lokalem Verzeichnis synchronisieren:
rsync –delete /mnt/www/beispiel.de /var/backup/beispiel.de
4. Verzeichis un-mounten
sudo umount /mnt/www/beispiel.de/
Das Ganze passt in ein kleines Skript. Wenn dann (was ich mit der betroffenen Präsenz noch nicht versucht habe) der Server einen ssh-key-import zulässt, kann man dieses dann über cron einbinden.
Tags: 1und1, backup, sftp, ssh, sshfs
Posted by Patrick Krusenotto on Mär 13, 2008 in
Allgemein,
Perl
Lisp wurde schon einmal als programmierbare Programiersprache bezeichnet. In engeren Genzen trifft dies auch auf Perl zu.
Es gibt in Perl ein Feature namens “Attributes”, das selten verwendet wird. Dieses Feature habe ich für ein Mini-Framework eingesetzt. In diesem Framework wollte ich erreichen, daß bestimmte, von mir mit “Session” Attributierte Variablen in der Session gespeichert werden, sodaß sie beim nächsten Request unverändert zur Verfügung stehen. Dadurch will ich erreichen, daß ich Werte nicht mehr explizit durch $name = $session->param(’name’) und Anderem, wie $session->flush() einlesen und speichern muss.
Im Anwendungscode sieht das zum Beispiel so aus:
my $name : Session('Name') ;
mit dieser Deklaration will ich dem System mitteilen, daß ich erwarte, daß diese Variable aus der Session geholt wird und am Ende der Bearbeitung des Requests wieder dort abgelegt wird.
Um dies zu erreichen, liegt im Framework eine sub MODIFY_SCALAR_ATTRIBUTE vor. Perl ruft diese Routine auf, sobald eine Variable mit einem Attribut (das ist hier der String “Session(Name)” ) versehen ist. Den Code der das Handling der Session vornimmt, stelle ich hier nicht vor, sondern nur die Routine, die die Attribut-Behandlung vornimmt.
my @SESSION_VARS;
sub MODIFY_SCALAR_ATTRIBUTES {
my ($mod, $ref, $attr) = @_;
my ($name) = $attr =~ /\((.*)\)/;
push @SESSION_VARS, {$name => $ref} ;
return ();
}
An MODIFY_SCALAR_ATTRIBUTES übergibt perl im Moment der Einrichtung der Variablen den Modulnamen, die Referenz auf die Variable und das Attribut.
Die Beispiel-Sub sammelt die Namen der Session-Variablen alle zusammen, um sie später unter Anwendung einer sub END { } geeignet zu behandeln.
Auf diese Weise kann ich Session-Werte so behandeln, als gehörten Sie in einen Übergeordneten Namensraum um den ich mich nicht kümmern muss.
Ausführlicheres zum Thema findet man unter Linux mit “man attributes”
Tags: Attribute, Perl
Posted by Patrick Krusenotto on Feb 29, 2008 in
Allgemein
Gefunden bei http:://xkcd.com :

Posted by Patrick Krusenotto on Nov 30, 2007 in
Allgemein,
Perl
Folgendes Plugin ermöglicht die Suche im CPAN mit Mozilla. Um es zu aktivierten, muss die Datei unter $HOME/.mozilla/firefox/xxxxxxxx.default/searchplugins abgelegt werden.
Unter windows ist C:\Eigene Dateien\Mozilla firefox\Searchplugins der richtige Ort
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"
xmlns:moz="http://www.mozilla.org/2006/browser/search/">
<ShortName>CPAN Search
<Description>search for CPAN modules
<InputEncoding>iso-8859-1
<Image width="16" height="16"> data:image/x-icon;base64,AAABAAEAEBAAAAAAAAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAwAAAAAAAAAAAAAAAAAAA
AAAAAAD///8AewAAAAB7AAB7ewAAAAB7AHsAewAAe3sAvb29AHt7ewD/AAAAAP8AAP//AAAAAP8A
/wD/AAD//wAAAAAA////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////
///////////////w/////////////////////zNs/L/vGObX6/D22wAA+ukAAvjDYWz8A3Rl+AJD
b/ACciDgAVzQ4AMRAOYPv+YnHwACB/8ABof/MoDj/zHg
<Url type="text/html" method="get" template="http://search.cpan.org/search">
<Param name="query" value="{searchTerms}"/>
</Url>
</OpenSearchDescription>
Posted by Patrick Krusenotto on Nov 28, 2007 in
Allgemein
Ja, richtig gelesen: Mit Emacs kann man CGI-Programierung unter Apache betreiben. Und das ist sogar ziemlich einfach! Die neue Version 22 unterstützt in eLisp-Programmen das Unix-”Shebang” und die Kommandozeilenoption “–script”. Damit kann man nun Folgendes veranstalten:
Ein Skript wie dieses hier
#!/usr/bin/emacs --script
(princ "Content-type: text/html\n\n")
(defun h1(x)
(concat "<h1>" x "</h1>"))
(defun p(x)
(concat "<p>" x "</p>"))
(princ
(concat (h1 "Emacs-Test")
(p "This is served by Emacs 22")))
im cgi-bin -Verzeichnis des Apache ablegen. Nicht vergessen, Ausführungsrechte zu erteilen.
Und schon kann man Webseiten in Emacs-Lisp programmieren. Natürlich fehlt noch einiges. da so gut wie Alles zu Fuß gemacht werden muss.
Was das soll? Nun, Emacslisp ist bestimmt das am weitesten verbreitete Lisp auf der Welt und von den unbestreitbaren Vorteilen der Sprache kann natürlich auch die Internet-Programmierung profitieren. Die Geschwindigkeit ist übrigens auch im CGI-Betrieb überaschend hoch. Hier kann man sich von der kurzen Ladezeit überzeugen.