Privacy Policy Cookie Policy Terms and Conditions Prolog (Programmiersprache) - Wikipedia

Prolog (Programmiersprache)

aus Wikipedia, der freien Enzyklopädie

Prolog („Programming in Logic“, auch frankophon „Prologue“) ist eine Programmiersprache, die maßgeblich von Alain Colmerauer, einem französischen Informatiker, Anfang der 1970er Jahre entwickelt wurde und zur Familie der deklarativen Programmierung zählt. Sie ist eine Vertreterin der logischen Programmiersprachen. Ursprüngliche Prologvarianten arbeiten vollständig auf Basis des Edinburgh Standards, bei aktuelleren Versionen wird dies aber nicht mehr konsequent durchgehalten.

Man kann die Sprache als „Maschinensprache eines Logik-Prozessors“ bezeichnen, da sie auf den mathematischen Grundlagen der Prädikatenlogik beruht. Ein Prolog-Programm ist eine Sammlung von so genannten Horn-Klauseln.

Ein Prolog-Interpreter wurde erstmals in Lisp programmiert.

Inhaltsverzeichnis

[Bearbeiten] Grundprinzip

Prolog-Programme bestehen aus einer Datenbasis, die Fakten und Regeln umfasst. Der Benutzer formuliert Anfragen an diese Datenbasis. Der Prolog-Interpreter benutzt die Fakten und Regeln, um systematisch eine Antwort zu finden. Ein positives Resultat bedeutet, dass die Antwort logisch ableitbar ist. Ein negatives Resultat bedeutet nur, dass aufgrund der Datenbasis keine Antwort gegeben werden kann. Dies hängt eng mit der Closed world assumption zusammen (siehe unten).

Das typische erste Programm in Prolog ist nicht wie in prozeduralen Programmiersprachen ein Hallo-Welt-Beispiel, sondern eine Datenbasis mit Stammbauminformationen.

Folgendes Beispiel repräsentiert den Stammbaum einer kleinen Familie. Die Aussage mann(tobias) liest sich als: Tobias ist ein Mann. vater(tobias, frank) wird hier verwendet als: Tobias ist der Vater von Frank.

   mann(adam).
   mann(tobias).
   mann(frank).
   frau(eva).
   frau(daniela).
   frau(ulrike).
   vater(adam,tobias).
   vater(tobias,frank).
   vater(tobias,ulrike).
   mutter(eva,tobias).
   mutter(daniela,frank).
   mutter(daniela,ulrike).

In einem Prolog-Interpreter können nun interaktiv Anfragen an die Datenbasis gestellt werden. Das Ausführen eines Prolog-Programms bedeutet immer das Stellen einer Anfrage. Das System antwortet entweder mit yes. oder no., abhängig davon, ob die Anfrage bewiesen werden konnte, oder es gibt zusätzlich eine Liste von Variablenbelegungen an. Variablen sind in Prolog alle Token, die mit einem Großbuchstaben beginnen. Beispiel:

   ?- mann(tobias).
   yes.
   ?- mann(heinrich).
   no.
   ?- frau(X).
   X=eva ;
   X=daniela ;
   X=ulrike ;
   no. (keine weiteren Antworten).

Das System kann nur positive Antworten zu Anfragen geben zu denen es Fakten in der Datenbasis vorliegen hat. Wenn etwas fehlt ist die Antwort no (nein), die bedeutet, dass aufgrund der Datenbank nichts ableitbar ist (Closed world assumption). Im Beispiel oben ist dies durch die Anfrage

   ?- mann(heinrich).
   no.

illustriert (heinrich kann in der Datenbasis nicht gefunden werden).

Zusätzlich zu der Möglichkeit, Fakten zu spezifizieren, bietet Prolog die Möglichkeit, Regeln zu formulieren. Der Regeloperator :- ist dabei wie ein umgedrehter Implikationspfeil zu lesen. Beispiel:

   grossvater(X,Y) :-
       vater(X,Z),
       vater(Z,Y).

Die Regel besagt: X ist Großvater von Y, wenn es ein Z gibt, so dass X Vater von Z ist und Z Vater von Y. Damit ist der Großvater väterlicherseits definiert. Eine zweite Regel für den Großvater mütterlicherseits sieht so aus:

   grossvater(X,Y) :-
       vater(X,Z),
       mutter(Z,Y).

Regeln mit gleichem Head, das heißt gleichem Term vor dem Regeloperator, werden als oder-verknüpfte Alternativen betrachtet. Jetzt sind Anfragen wie die folgenden möglich:

   ?- grossvater(adam,ulrike).
   yes.
   ?- grossvater(X,frank).
   X=adam

[Bearbeiten] Weitere Techniken

Entscheidend für die Prolog-Programmierung sind die Techniken der Rekursion und die Nutzung von Listen.

Ist die Rekursion in den meisten Programmiersprachen nur eine zusätzliche Variante zur Iteration, ist sie bei der Prolog-Programmierung die einzige Möglichkeit, „Schleifen“ zu produzieren. Benötigt man in obigem Beispiel eine allgemeine „Vorfahr“-Relation, wird das wie folgt realisiert:

   vorfahr(X,Z) :-
       elternteil(X,Z).
  
   vorfahr(X,Z) :-
       elternteil(X,Y),
       vorfahr(Y,Z).

Dies lässt sich wie folgt lesen: X ist ein Vorfahr von Z, wenn X Elternteil von Z ist (Regel 1) oder es ein Y gibt, das Vorfahr von Z ist und gleichzeitig X Elternteil von Y (Regel 2) (Es wurde hier elternteil statt mutter und vater verwendet. elternteil(X,Y) :- mutter(X,Y); vater(X,Y).).

Auch Listen sind ein entscheidender Bestandteil von Prolog. Die meisten Prolog-Implementationen bringen dafür viele Funktionen mit (Anhängen von Werten, Anzahl der Werte, etc.), die sich aber auch alle selbst bauen lassen. In einer gedachten Familienstruktur muss die Anzahl der Kinder ja variabel sein. Folgendes wäre denkbar:

   familie(heinz,jutta,[peter,laura]).
   familie(karl,gertrud,[]).

Dann ließen sich z.B. mit einer Abfrage alle Familienväter ohne Kinder anzeigen:

   ?- familie(X, _, []).
   X=karl


Eine weitere Eigenschaft und Besonderheit gegenüber anderen Programmiersprachen ist, dass Prolog in der Lage ist während der Laufzeit seine vorhandene Datenbank zu erweitern oder zu löschen. Ein Beispiel für das Löschen eines einzelnen Elements:

    auto(bmw,rot).
    auto(bmw,blau).
    autofarbe(Automarke,X):-
       retract(auto(bmw,_)),
       auto(Automarke,X).

Die Abfrage:

    ?- auto(bmw,X).

ergibt (anfänglich) ganz normal:

    X=rot;
    X=blau;
    No

Die Abfrage:

    ?- autofarbe(bmw,X).

würde beim ersten mal:

    X=blau;
    No

beim zweiten Mal nur noch:

    No

liefern, da die Informationen:

    auto(bmw,rot).
    auto(bmw,blau).

aus der Datenbank gelöscht wurden. Auch ?- auto(bmw,X) liefert jetzt nur noch No. Zum Löschen aller gleichen Elemente (also z. B. auto()) auf einmal benutzt man retractall(), zum Ausgeben asserta() (oben in der Datenbank) und assertz() (unten in der Datenbank).

[Bearbeiten] Beispiele

[Bearbeiten] Lösen eines mathematischen Rätsels

 ABB -  CD = EED
  -     -     *
  FD +  EF =  CE
  =     =     =
 EGD *  FH = ???  

A bis H stehen jeweils für eine Ziffer 0 bis 9, wobei nicht klar ist, welche Zahl an welchen Buchstaben gebunden ist. Gesucht ist die Zahl, die bei den Fragezeichen stehen muss. Dieses Problem ist in Prolog sehr einfach zu lösen. Man schreibt zunächst eine Regel, die bewirkt, dass A bis H später mit allen möglichen Kombinationen von 0 bis 9 belegt werden (Permutation):

 gen(A,B,C,D,E,F,G,H) :- permutation([A,B,C,D,E,F,G,H,_,_],[0,1,2,3,4,5,6,7,8,9]).

Nun muss man nur die fünf entstehenden Gleichungen (ABB – CD = EED, FD + EF = CE, ABB – FD = EGD, CD – EF = FH und EED * CE = EGD * FH = X) in Prolog-Syntax schreiben:

 gl1(A,B,C,D,E) :- ((A * 100 + B * 10 + B) - (C * 10 + D)) =:= (E * 100 + E * 10 + D).
 gl2(C,D,E,F) :- ((F * 10 + D) + (E * 10 + F)) =:= (C * 10 + E).
 gl3(A,B,D,E,F,G) :- ((A * 100 + B * 10 + B) - (F * 10 + D)) =:= (E * 100 + G * 10 + D).
 gl4(C,D,E,F,H) :- ((C * 10 + D) - (E * 10 + F)) =:= (F * 10 + H).
 gl5(C,D,E,F,G,H,X) :- ((E * 100 + E * 10 + D) * (C * 10 + E)) =:= 
     ((E * 100 + G * 10 + D) * (F * 10 + H)), X is ((E * 100 + G * 10 + D) * (F * 10 + H)).

Wenn einen nur X interessiert legt man sich eine Lösungsregel an, die alles zusammenführt und X ausgibt:

 loesung :- gen(A,B,C,D,E,F,G,H), gl1(A,B,C,D,E), gl2(C,D,E,F),
     gl3(A,B,D,E,F,G), gl4(C,D,E,F,H), gl5(C,D,E,F,G,H,X), write(X).

Gibt man nun die Abfrage loesung. ein, wird die Lösung ausgegeben. Wie man sieht, benötigt man zur Lösung dieses Problems fast keine Programmierkenntnisse über Schleifen oder ähnliches, sondern gibt nur die Fakten ein und welches Ergebnis man benötigt. Prolog steht in der Abstraktionshierarchie aus genau diesem Grund über imperativen und objektorientierten Sprachen.

[Bearbeiten] Bearbeitung hierarchischer Strukturen

Eine häufig gestellte Aufgabe an Programmiersprachen ist die Verarbeitung hierarchischer Strukturen, wie z. B. SGML oder XML. Insbesondere für XML bildet Prolog eine sehr wirkungsvolle und ausdrucksstarke Alternative zu der verbreitetsten Verarbeitungssprache XSLT.

Ein typischer XML-Baum wie

   <buch titel="Peer Gynt">
     <autor name="Henrik Ibsen" nat="norwegisch"/>
     ...
   </buch>

wird unter Prolog als rekursive Liste von Elementen element(TagName, Attribute, Kinder) dargestellt.

   [element(buch, [titel='Peer Gynt'], [
      element(autor, [name='Henrik Ibsen', nat='norwegisch'], []),
      ...]
   ]

Ein sehr einfaches Paradigma (untere drei Klauseln) erlaubt es, jeden Baum rekursiv zu durchlaufen. Folgende Beispiele löschen (oberster Klausel mit delete) und konkatenieren (zweiter Klausel von oben mit concat) bestimmte Tags. Der erste Unifikator ist die Operation (delete oder concat), der zweite die zu bearbeitende Struktur, der dritte das spezifierte Tag, der vierte der Ergebnisbaum. append ist ein Befehl zum konkatenieren von Listen.

   transform(delete, [element(DelTag, _, _) | Siblings], DelTag, ResTree):-
      transform(delete, Siblings, DelTag, ResTree).
   transform(concat, [Element1, Element2 | Siblings], ConTag, ResTree):-
      Element1 = element(Contag, Attr, Children1),
      Element2 = element(Contag, _, Children2),
      append(Children1, Children2, Children),
      transform(concat, [element(ConTag, Attr, Children) | Siblings], ConTag, ResTree).

   transform(_, [], _, []).
    
   transform(Trans, [element(CTag, Attr, Children) | Siblings], Tag, ResTree):-
      \+ Tag = CTag,
      transform(Trans, Children, Tag, ResChildren),
      transform(Trans, Siblings, Tag, ResSiblings),
      ResTree = [element(CTag, Attr, ResChildren) | ResSiblings]).
    
   transform(_, [Atom], _, [Atom]):-
      atomic(Atom).

Stößt der Backtracker bei der Operation delete auf ein Tag, das wie das zu löschende heißt, so wird dieses entfernt und bei den Nachbarn weitergesucht. Ein entsprechender Aufruf ist z. B. transform(delete, Tree, autor, ResTree)., der alle Autoren entfernt.

Ähnlich kann man durch transform(concat, Tree, paragraph, ResTree). alle nebeneinanderstehenden Paragraphen miteinander verschmelzen. Dazu werden zunächst deren Inhalte konkateniert, daraus eine neue Paragraphstruktur erzeugt und diese weiterverarbeitet.

[Bearbeiten] Planungssysteme

Planungssysteme suchen eine Möglichkeit, von einem Ausgangsszustand in einen gewünschten Zielzustand zu gelangen. Sie lassen sich für die Suche von Straßen- oder Verkehrsverbindungen, aber auch für allgemeinere Problemstellungen einsetzen. Zunächst der allgemeinste Ansatz für eine "blinde Tiefensuche" (d.h. es ist unbekannt, ob der einzelne Schritt auch näher zum Ziel führt):

weg(Ziel,Ziel,Zustandsliste):-
  write(Zustandsliste),nl.               /* Ziel erreicht, Abbruch der Rekursion und Ausgabe */
weg(Start,Ziel,Zustandsliste):-          /* Es gibt einen Weg vom Start zum Ziel, wenn... */
  operator(Op),                          /* ...es einen Operator gibt,... */
  anwendbar(Op,Start),                   /* ...der im Startzustand anwendbar ist,... */
  fuehrt_zu(Op,Start,Neu),               /* ...von dort zu einem neuen Zustand fuehrt,... */
  not(member(Neu,Zustandsliste)),        /* ...der noch nie da war (Verhinderung von Schleifen)... */
  zulaessig(Neu),                        /* ...und zulaessig ist,... */
  weg(Neu,Ziel,[Neu|Zustandsliste]).     /* ...und es von dort einen Weg zum Ziel gibt. */

Nur die Prädikate operator, anwendbar, fuehrt_zu und zulaessig sowie die Beschreibung eines Zustands sind problemspezifisch zu formulieren. Aufgerufen wird das Prädikat mit einer Zustandsliste, die den Anfangszustand enthält.

Abhängig vom Problemtyp lässt sich einiges vereinfachen und/oder weglassen; für eine Wegesuche in einem Straßennetz ergibt sich z.B.

weg(Ziel,Ziel,Ortsliste):-
  write(Ortsliste),nl.                   /* Ziel erreicht, Abbruch der Rekursion und Ausgabe */
weg(Start,Ziel,Ortsliste):-              /* Es gibt einen Weg vom Start zum Ziel, wenn... */
  strasse(Start,Neu),                    /* ...es eine Strasse vom Start zu einem neuen Ort gibt,... */
  not(member(Neu,Ortsliste)),            /* ...in dem man noch nicht war (Verhinderung von Schleifen),... */
  weg(Neu,Ziel,[Neu|Ortsliste]).         /* ...und von dem es einen Weg zum Ziel gibt. */

Bei realen Problemen führt eine blinde Suche selten zum Ziel; man benutzt eine Breitensuche, bei der alle vom Start aus erreichbaren neuen Zustände ermittelt, mit einer "Heuristikfunktion" bewertet und nur der beste ("Heuristische Suche") oder eine sortierte Liste der besten ("Best-first-Suche") weiterverfolgt werden. (Die einfache heuristische Suche kann dazu führen, dass nicht immer die optimale Lösung gefunden wird, da bestimmte Lösungsschritte, die fälschlicherweise als ungünstig aussortiert wurden, sich als bessere Lösung ergeben würden.) Die Kunst liegt in der richtigen problemspezifischen Formulierung der Heuristikfunktion. In vielen Fällen hilft die "A-Heuristik", das ist die Summe aus bisher erbrachtem Aufwand und geschätztem Restaufwand zum Ziel (z.B. zurückgelegte Fahrtstrecke + Luftliniendistanz zum Zielort):

weg(Ziel,Ziel,Ortsliste,Strecke):-
  write(Ortsliste),nl,write(Strecke),nl.                 /* Ziel erreicht, Abbruch der Rekursion und Ausgabe */
weg(Start,Ziel,Ortsliste,Strecke):-                      /* Es gibt einen Weg vom Start zum Ziel, wenn... */
  findall(Ort,strasse(Start,Ort),Neuliste),              /* ...es eine Liste erreichbarer neuer Orte gibt,... */
  bewerte(Neuliste,Start,Strecke,Ziel,BewerteteListe),   /* ...von denen jeder bewertet und ... */
  sort(BewerteteListe,SortierteListe),                   /* ...durch Sortieren der Liste... */
  member([_,Sgesamt,Neu],SortierteListe),                /* ...der beste gesucht wird,... */
  not(member(Neu,Ortsliste)),                            /* ...in dem man noch nicht war,... */
  weg(Neu,Ziel,[Neu|Ortsliste],Sgesamt).                 /* ...und von dem es einen Weg zum Ziel gibt. */

Jedes Element von BewerteteListe hat die Struktur [Heuristikwert,gesamte Fahrtstrecke,Ort]; zur Berechnung der A-Heuristik sind die bisherige Strecke, der letzte Ort und der Zielort (Luftlinie!) erforderlich.

[Bearbeiten] Einsteins Rätsel

Einstein hat dieses Rätsel im 19. Jahrhundert verfasst. Er behauptete, 98% der Weltbevölkerung seien nicht in der Lage, es zu lösen.

  1. Es gibt 5 Häuser mit je einer anderen Farbe.
  2. In jedem Haus wohnt eine Person anderer Nationalität.
  3. Jeder Hausbewohner bevorzugt ein bestimmtes Getränk, raucht eine bestimmte Zigarettenmarke und hält ein bestimmtes Haustier.
  4. Keine der 5 Personen trinkt das gleiche Getränk, raucht die gleichen Zigaretten oder hält das gleiche Tier wie seine Nachbarn.

Frage: Wem gehört der Fisch?

Hinweise:

  • Der Brite lebt im roten Haus.
  • Der Schwede hält einen Hund.
  • Der Däne trinkt gern Tee.
  • Das grüne Haus steht links vom weißen Haus.
  • Der Besitzer des grünen Hauses trinkt Kaffee.
  • Die Person, die Pall Mall raucht, hält einen Vogel.
  • Der Mann, der im mittleren Haus wohnt, trinkt Milch.
  • Der Besitzer des gelben Hauses raucht Dunhill.
  • Der Norweger wohnt im 1. Haus.
  • Der Marlboro-Raucher wohnt neben dem, der eine Katze hält.
  • Der Mann, der ein Pferd hält, wohnt neben dem, der Dunhill raucht.
  • Der Winfield-Raucher trinkt gern Bier.
  • Der Norweger wohnt neben dem blauen Haus.
  • Der Deutsche raucht Rothmans.
  • Der Marlboro-Raucher hat einen Nachbarn, der Wasser trinkt.

Lösung:

Jedes Haus ist eine Liste der Form [Farbe, Nationalitaet, Getraenk, Zigarettenmarke, Haustier].

Zuerst vier einfache Hilfsprädikate zur Listenbearbeitung:

erstes(E,[E|_]).
mittleres(M,[_,_,M,_,_]).
links(A,B,[A|[B|_]]).
links(A,B,[_|R]):-links(A,B,R).
neben(A,B,L):-links(A,B,L);links(B,A,L).

Lösungsprädikat:

run:-
  X = [_,_,_,_,_],                                /* Es gibt (nebeneinander) 5 (noch unbekannte) Häuser */
  member([rot,brite,_,_,_],X),                    /* Der Brite lebt im roten Haus */
  member([_,schwede,_,_,hund],X),                 /* Der Schwede hält einen Hund */
  member([_,daene,tee,_,_],X),                    /* Der Däne trinkt gern Tee */
  links([gruen,_,_,_,_],[weiss,_,_,_,_],X),       /* Das grüne Haus steht links vom weißen Haus */
  member([gruen,_,kaffee,_,_],X),                 /* Der Besitzer des grünen Hauses trinkt Kaffee */
  member([_,_,_,pallmall,vogel],X),               /* Die Person, die Pall Mall raucht, hält einen Vogel */
  mittleres([_,_,milch,_,_],X),                   /* Der Mann, der im mittleren Haus wohnt, trinkt Milch */
  member([gelb,_,_,dunhill,_],X),                 /* Der Besitzer des gelben Hauses raucht Dunhill */
  erstes([_,norweger,_,_,_],X),                   /* Der Norweger wohnt im 1. Haus */
  neben([_,_,_,marlboro,_],[_,_,_,_,katze],X),    /* Der Marlboro-Raucher wohnt neben dem, der eine Katze hält */
  neben([_,_,_,_,pferd],[_,_,_,dunhill,_],X),     /* Der Mann, der ein Pferd hält, wohnt neben dem, der Dunhill raucht */
  member([_,_,bier,winfield,_],X),                /* Der Winfield-Raucher trinkt gern Bier */
  neben([_,norweger,_,_,_],[blau,_,_,_,_],X),     /* Der Norweger wohnt neben dem blauen Haus */
  member([_,deutsche,_,rothmans,_],X),            /* Der Deutsche raucht Rothmans */
  neben([_,_,_,marlboro,_],[_,_,wasser,_,_],X),   /* Der Marlboro-Raucher hat einen Nachbarn, der Wasser trinkt */
  member([_,N,_,_,fisch],X),                      /* Der mit der Nationalität N hat einen Fisch */
  write(X),nl,                                    /* Ausgabe aller Häuser */
  write('Der '),write(N),write(' hat einen Fisch als Haustier.'),nl.   /* Antwort auf die Frage */

[Bearbeiten] Definite Clause Grammar

Um Regeln für Parser zu schreiben haben die meisten Prologsysteme einen Präprozessor implementiert, der es erlaubt, die Regeln in einer lesbareren Form zu notieren, die in der Form den Regeln entsprechen, die man verwendet um eine kontextfreie Sprache zu beschreiben. Der Präprozessor ergänzt Platzhalter und erzeugt die oben erwähnten Prolog-Logik-Formeln. Durch Übergabe weiterer Attribute ist es möglich mit Definite Clause Grammars auch komplexere Sprachen als die kontextfreien zu beschreiben.

[Bearbeiten] Anwendungsgebiete

In den 1980er Jahren spielte die Sprache eine wichtige Rolle beim Bau von Expertensystemen. Die Sprache wird auch heute noch in den Bereichen Computerlinguistik und Künstliche Intelligenz verwendet. Außerdem gibt es einige kommerzielle Anwendungen im Bereich des Systemmanagements, bei denen asynchrone Ereignisse (Events) mit Hilfe von Prolog oder darauf basierenden proprietären Erweiterungen verarbeitet werden. Ein Beispiel hierzu ist das Produkt „Tivoli Enterprise Console“ von IBM, das auf BIM-Prolog basiert.

[Bearbeiten] Siehe auch

Wikibooks: Prolog – Lern- und Lehrmaterialien

[Bearbeiten] Literatur

  • H. Göhner; B. Hafenbrack: Arbeitsbuch PROLOG. DÜMMLER, Bonn, 1995, ISBN 3-427-46863-1 (PDF-Version)
  • William F. Clocksin, Christopher S. Mellish: Programming in Prolog. Springer, Berlin, 2003, ISBN 3-540-00678-8
  • Leon Sterling, Ehud Shapiro: The Art of Prolog. Advanced Programming Techniques. MIT Press, Cambridge, 1994, ISBN 0-262-69163-9
  • Ivan Bratko: Prolog Programming for Artificial Intelligence. Addison-Wesley, Bonn, 2000, ISBN 0-201-40375-7
  • Ivan Bratko: Prolog. Programmierung für Künstliche Intelligenz. Addison-Wesley, Bonn, 1987, ISBN 3-925118-63-2
  • William F. Clocksin: Clause and Effect. Prolog Programming for the Working Programmer. Springer, Berlin, 2005, ISBN 3-540-62971-8
  • Uwe Schöning: Logik für Informatiker. Spektrum, Heidelberg, 2000, ISBN 3-8274-1005-3
  • Richard A. O'Keefe: The Craft of Prolog. MIT Press, Cambridge, 1990, ISBN 0-262-15039-5
  • Patrick Blackburn, Johan Bos, Kristina Striegnitz: Learn Prolog Now!. College Publications, 2006, ISBN 1-904987-17-6
  • Esther König, Roland Seiffert: Grundkurs PROLOG fur Linguisten. UTB Linguistik, 1989, ISBN 3-7720-1749-5

[Bearbeiten] Weblinks

[Bearbeiten] Tutorials und Kurse

[Bearbeiten] Interpreter und Werkzeuge

Dieser Artikel wurde in die Liste der Lesenswerten Artikel aufgenommen.
THIS WEB:

aa - ab - af - ak - als - am - an - ang - ar - arc - as - ast - av - ay - az - ba - bar - bat_smg - be - bg - bh - bi - bm - bn - bo - bpy - br - bs - bug - bxr - ca - cbk_zam - cdo - ce - ceb - ch - cho - chr - chy - closed_zh_tw - co - cr - cs - csb - cu - cv - cy - da - de - diq - dv - dz - ee - el - eml - en - eo - es - et - eu - fa - ff - fi - fiu_vro - fj - fo - fr - frp - fur - fy - ga - gd - gl - glk - gn - got - gu - gv - ha - haw - he - hi - ho - hr - hsb - ht - hu - hy - hz - ia - id - ie - ig - ii - ik - ilo - io - is - it - iu - ja - jbo - jv - ka - kg - ki - kj - kk - kl - km - kn - ko - kr - ks - ksh - ku - kv - kw - ky - la - lad - lb - lbe - lg - li - lij - lmo - ln - lo - lt - lv - map_bms - mg - mh - mi - mk - ml - mn - mo - mr - ms - mt - mus - my - mzn - na - nah - nap - nds - nds_nl - ne - new - ng - nl - nn - no - nov - nrm - nv - ny - oc - om - or - os - pa - pag - pam - pap - pdc - pi - pih - pl - pms - ps - pt - qu - rm - rmy - rn - ro - roa_rup - roa_tara - ru - ru_sib - rw - sa - sc - scn - sco - sd - se - searchcom - sg - sh - si - simple - sk - sl - sm - sn - so - sq - sr - ss - st - su - sv - sw - ta - te - test - tet - tg - th - ti - tk - tl - tlh - tn - to - tokipona - tpi - tr - ts - tt - tum - tw - ty - udm - ug - uk - ur - uz - ve - vec - vi - vls - vo - wa - war - wo - wuu - xal - xh - yi - yo - za - zea - zh - zh_classical - zh_min_nan - zh_yue - zu

Static Wikipedia 2008 (no images)

aa - ab - af - ak - als - am - an - ang - ar - arc - as - ast - av - ay - az - ba - bar - bat_smg - bcl - be - be_x_old - bg - bh - bi - bm - bn - bo - bpy - br - bs - bug - bxr - ca - cbk_zam - cdo - ce - ceb - ch - cho - chr - chy - co - cr - crh - cs - csb - cu - cv - cy - da - de - diq - dsb - dv - dz - ee - el - eml - en - eo - es - et - eu - ext - fa - ff - fi - fiu_vro - fj - fo - fr - frp - fur - fy - ga - gan - gd - gl - glk - gn - got - gu - gv - ha - hak - haw - he - hi - hif - ho - hr - hsb - ht - hu - hy - hz - ia - id - ie - ig - ii - ik - ilo - io - is - it - iu - ja - jbo - jv - ka - kaa - kab - kg - ki - kj - kk - kl - km - kn - ko - kr - ks - ksh - ku - kv - kw - ky - la - lad - lb - lbe - lg - li - lij - lmo - ln - lo - lt - lv - map_bms - mdf - mg - mh - mi - mk - ml - mn - mo - mr - mt - mus - my - myv - mzn - na - nah - nap - nds - nds_nl - ne - new - ng - nl - nn - no - nov - nrm - nv - ny - oc - om - or - os - pa - pag - pam - pap - pdc - pi - pih - pl - pms - ps - pt - qu - quality - rm - rmy - rn - ro - roa_rup - roa_tara - ru - rw - sa - sah - sc - scn - sco - sd - se - sg - sh - si - simple - sk - sl - sm - sn - so - sr - srn - ss - st - stq - su - sv - sw - szl - ta - te - tet - tg - th - ti - tk - tl - tlh - tn - to - tpi - tr - ts - tt - tum - tw - ty - udm - ug - uk - ur - uz - ve - vec - vi - vls - vo - wa - war - wo - wuu - xal - xh - yi - yo - za - zea - zh - zh_classical - zh_min_nan - zh_yue - zu -

Static Wikipedia 2007:

aa - ab - af - ak - als - am - an - ang - ar - arc - as - ast - av - ay - az - ba - bar - bat_smg - be - bg - bh - bi - bm - bn - bo - bpy - br - bs - bug - bxr - ca - cbk_zam - cdo - ce - ceb - ch - cho - chr - chy - closed_zh_tw - co - cr - cs - csb - cu - cv - cy - da - de - diq - dv - dz - ee - el - eml - en - eo - es - et - eu - fa - ff - fi - fiu_vro - fj - fo - fr - frp - fur - fy - ga - gd - gl - glk - gn - got - gu - gv - ha - haw - he - hi - ho - hr - hsb - ht - hu - hy - hz - ia - id - ie - ig - ii - ik - ilo - io - is - it - iu - ja - jbo - jv - ka - kg - ki - kj - kk - kl - km - kn - ko - kr - ks - ksh - ku - kv - kw - ky - la - lad - lb - lbe - lg - li - lij - lmo - ln - lo - lt - lv - map_bms - mg - mh - mi - mk - ml - mn - mo - mr - ms - mt - mus - my - mzn - na - nah - nap - nds - nds_nl - ne - new - ng - nl - nn - no - nov - nrm - nv - ny - oc - om - or - os - pa - pag - pam - pap - pdc - pi - pih - pl - pms - ps - pt - qu - rm - rmy - rn - ro - roa_rup - roa_tara - ru - ru_sib - rw - sa - sc - scn - sco - sd - se - searchcom - sg - sh - si - simple - sk - sl - sm - sn - so - sq - sr - ss - st - su - sv - sw - ta - te - test - tet - tg - th - ti - tk - tl - tlh - tn - to - tokipona - tpi - tr - ts - tt - tum - tw - ty - udm - ug - uk - ur - uz - ve - vec - vi - vls - vo - wa - war - wo - wuu - xal - xh - yi - yo - za - zea - zh - zh_classical - zh_min_nan - zh_yue - zu

Static Wikipedia 2006:

aa - ab - af - ak - als - am - an - ang - ar - arc - as - ast - av - ay - az - ba - bar - bat_smg - be - bg - bh - bi - bm - bn - bo - bpy - br - bs - bug - bxr - ca - cbk_zam - cdo - ce - ceb - ch - cho - chr - chy - closed_zh_tw - co - cr - cs - csb - cu - cv - cy - da - de - diq - dv - dz - ee - el - eml - en - eo - es - et - eu - fa - ff - fi - fiu_vro - fj - fo - fr - frp - fur - fy - ga - gd - gl - glk - gn - got - gu - gv - ha - haw - he - hi - ho - hr - hsb - ht - hu - hy - hz - ia - id - ie - ig - ii - ik - ilo - io - is - it - iu - ja - jbo - jv - ka - kg - ki - kj - kk - kl - km - kn - ko - kr - ks - ksh - ku - kv - kw - ky - la - lad - lb - lbe - lg - li - lij - lmo - ln - lo - lt - lv - map_bms - mg - mh - mi - mk - ml - mn - mo - mr - ms - mt - mus - my - mzn - na - nah - nap - nds - nds_nl - ne - new - ng - nl - nn - no - nov - nrm - nv - ny - oc - om - or - os - pa - pag - pam - pap - pdc - pi - pih - pl - pms - ps - pt - qu - rm - rmy - rn - ro - roa_rup - roa_tara - ru - ru_sib - rw - sa - sc - scn - sco - sd - se - searchcom - sg - sh - si - simple - sk - sl - sm - sn - so - sq - sr - ss - st - su - sv - sw - ta - te - test - tet - tg - th - ti - tk - tl - tlh - tn - to - tokipona - tpi - tr - ts - tt - tum - tw - ty - udm - ug - uk - ur - uz - ve - vec - vi - vls - vo - wa - war - wo - wuu - xal - xh - yi - yo - za - zea - zh - zh_classical - zh_min_nan - zh_yue - zu