Posts tagged ‘Prolog’

Lernen im dritten Quartal 2009

Damit habe ich mich im dritten Quartal 2009 beschäftigt:

  • Ich habe das Buch Subject to Change gelesen. Meine Eindrücke habe ich bereits im Blog beschrieben: Es geht darum, wie heute Produkte und Services entwickelt werden müssen. Das ganze passt wenig überraschend mit agilen Ansätzen wie Scrum ganz wunderbar zusammen.
  • Dann habe ich mich ziemlich intensiv mit Prolog beschäftigt und einige Beiträge in diesem Blog darüber veröffentlicht. Das war allemal horizonterweiternd – Prolog ist eben doch sehr anders als andere Programmiersprachen. Ich finde es nach wie vor Schade, dass Prolog heute ziemlich tot zu sein scheint und die Sprache nicht ernsthaft weiterentwickelt wird. Relektionen über Prolog.
  • In Prolog habe ich ann auch gleich ein kleines BDD-Framework gebaut und damit mein BDD-Verständnis verbessert.
  • Joseph Pelrine ist dafür verantwortlich, dass ich noch etwas über Teamdynamik gelernt habe: Hochproduktive Teams kochen.
  • Von einem Kunden habe ich mich belehren lassen, was die mögliche Produktivität verteilter Teams anbelangt.
  • Ein ganz kleines bisschen Perl habe ich gelernt. Und zumindest für kleine Skripte kann ich nicht erkennen, was an Perl schlecht sein soll.
  • In einem Projekt habe ich Selenium RC eingesetzt und den designierten Nachfolger Google Web-Driver.
  • Ich habe meine Rails-Kenntnisse deutlich vertieft. Dazu habe ich das Buch Rapid Web Development mit Ruby on Rails gelesen. Das ist nicht mehr ganz aktuell, aber ich hatte es ohnehin noch im Bücherregal stehen und für einen Überblick war es OK. Anschließend habe ich begonnen, eine echte (kleine) Anwendung in Rails zu programmieren und bin davon ganz angetan. Nur mit der Installtion unter Ubuntu hatte ich anfänglich einige Probleme.
  • Im Rails-Zusammenhang habe ich mich auch mit RSpec, Cucumber und Webrat beschäftigt. Auch das sieht für mich alles sehr schön und elegant aus. Das Oberflächentesten mit Webrat ist z.B. sehr schön einfach und schnell.
  • Bei einem Kunden habe ich mich etwas mit TestNG beschäftigt. Naja. Für TDD kann ich gegenüber JUnit keine Vorteile erkennen.
  • Ich habe mit dem Buch SOA in Praxis ein weiteres Buch aus dem Regal geholt, dass dort schon eine Weile stand. Mein Review dazu habe ich im Blog beschrieben. Das Buch war durchaus lohnenswert, auch wenn ich kein SOA-Fan bin.
  • Bei einem Kunden durfte ich erleben, wie Kanban in der Praxis eingesetzt wurde – zumindest anfänglich auch mit interessanten Schwierigkeiten.
  • Neben CSM-Kursen biete ich inzwischen auch CSPO-Kurse an. Bei der Vorbereitung des CSPO-Kurses habe ich viele Dinge über Product-Owner-Arbeit reflektiert und neu gelernt.
  • Und zum Beweis, dass ich noch nicht vollkommen Plem-Plem bin, habe ich auch etwas im Bereich außerhalb der IT gemacht. Ich habe beim Windsurfen Racejibe und Sinkerwende gelernt. OK, Racejibe geht bisher nur rechtsrum, aber das wird noch. Ich hoffe, auf einen milden und windreichen Jahresausklang.
  • Und dann habe ich mir von meinem kleinen Bruder zeigen lassen, wie Brustschwimmen und Kraulen richtig funktioniert. Und siehe da: Das einzige, was ich bisher richtig gemacht habe, dass ich eine Badehose anhatte. Immerhin erklärt es, warum ich so ein schlechter Schwimmer bin. Jetzt muss ich die richtige Technik “nur” noch üben.

Damit habe ich von dem, was ich mir für das dritte Quartal vorgenommen habe, fast alles geschafft. Nur Scala habe ich mir nicht angesehen. Ich fand es dann doch spannender einfach Rails zu programmieren und aus demselben Grund werde ich wahrscheinlich auch im vierten Quartal nicht dazu kommen, mir Scala anzusehen.

October 14, 2009 at 5:45 pm Leave a comment

BDD for Prolog: How To

ProSpec is a RSpec (http://rspec.info) inspired BDD framework for Prolog. I created it during my experiments with Prolog. I suspect that I did some strange things. I would be pleased by any feedback on how to improve ProSpec or the examples. Send me an E-Mail: stefan AT stefanroock DOT de

Every ProSpec specification starts with describe(FixtureName) and ends with end_describe. Within these two predicates every specification is declared with it(SpecName/SpecCode).

We start with a very simple BDD spec for bowling.

bowling_spec.pro:

:- ensure_loaded('ProSpec.pro').
:- ensure_loaded('bowling.pro').

:- describe('Bowling'). 

generate_bowling_hits(_, 0).
generate_bowling_hits(Hit, Count) :- bowling_hit(Hit), Count2 is Count-1, generate_bowling_hits(Hit, Count2).

:- it('should score 0 for gutter game'/(
	generate_bowling_hits(0, 20),
	bowling_score(Score),
	assert_that(Score, equals:0)
)). 

:- end_describe.

bowling.pro:

bowling_hit(Count).
bowling_score(0).

With “run_specs.” the BDD spec runs:

Bowling...
...should score 0 for gutter game (passed)
Passed:1

1 specs passed

In the next step with specify the next behaviour increment:

bowling_spec.pro:

:- ensure_loaded('ProSpec.pro').
:- ensure_loaded('bowling.pro').

:- describe('Bowling'). 

generate_bowling_hits(_, 0).
generate_bowling_hits(Hit, Count) :- bowling_hit(Hit), Count2 is Count - 1, generate_bowling_hits(Hit, Count2).

:- it('should score 0 for gutter game'/(
	generate_bowling_hits(0, 20),
	bowling_score(Score),
	assert_that(Score, equals:0)
)). 

:- it('should sum hits of a game'/(
	generate_bowling_hits(2, 20),
	bowling_score(Score),
	assert_that(Score, equals:40)
)). 

:- end_describe.

“run_specs.” shows that this spec fails:

Bowling...
...should score 0 for gutter game (passed)
...should sum hits of a game
	Expected 40 equal to 0

We extend the bowling implementation:

bowling_hit(Count) :- assert(bowling_hit_def(Count)).

bowling_score(Score) :- 
	findall(Hit, bowling_hit_def(Hit), Hits),
	sumlist(Hits, Score).

Now the spec succeeds, but only once. The reason is the growing number
of “bowling_hit_def” facts. To avoid this problem ProSpec offers “setup_spec”.

bowling_spec.pro:

:- ensure_loaded('ProSpec.pro').
:- ensure_loaded('bowling.pro').

:- describe('Bowling'). 

generate_bowling_hits(_, 0).
generate_bowling_hits(Hit, Count) :- bowling_hit(Hit), Count2 is Count - 1, generate_bowling_hits(Hit, Count2).

:- dynamic(setup_spec/0).

setup_spec :- 
	retractall(bowling_hit_def(_)).

:- it('should score 0 for gutter game'/(
	generate_bowling_hits(0, 20),
	bowling_score(Score),
	assert_that(Score, equals:0)
)). 

:- it('should sum hits of a game'/(
	generate_bowling_hits(2, 20),
	bowling_score(Score),
	assert_that(Score, equals:40)
)). 

:- end_describe.

Now the spec succeeds every time.

There is a bunch of other assert_that predicates in ProSpec.pro. Have a look
and proceed: http://github.com/stefanroock/ProSpec/

July 27, 2009 at 5:05 pm Leave a comment

Reflektionen über Prolog

Ich erkläre meine Experimente mit Prolog jetzt erstmal für beendet und schließe mit ein paar Reflektionen über Prolog. Es ist durchaus möglich, dass ich an der einen oder anderen Stelle einem Irrtum aufgesessen bin. In diesem Fall freue ich mich über entsprechende Kommentare.

  1. Prolog als Sprache fand ich ganz lustig, auch weil die Ansätze so anders sind als das, was man sonst so in Programmiersprachen vorfindet. In diesem Sinne war die Beschäftigung mit Prolog auf jeden Fall zur Horizonterweiterung nützlich.
  2. Für Prolog gibt es einen ISO-Standard, so dass Prolog-Systeme verschiedener Hersteller kompatibel sein sollten. Das ist eigentlich ganz nett. Aber je mehr Programmiersprachen mit ISO-Standard ich kennenlerne, umso mehr habe ich den Eindruck, dass eine ISO-Standardisierung die Fahrkarte ins Reich der toten Programmiersprachen ist.
  3. Möglicherweise ist das auch der Grund dafür, warum es keine Weiterentwicklung von Prolog zu geben scheint. Das finde ich persönlich schade, weil ich die Ansätze vielversprechend finde. Aber irgendwie müsste man die Konzepte noch in die heutige Zeit bringen.
  4. Und ein Nutzen wäre da. Soweit ich das überblicken kann, macht eine Rule-Engine ungefähr, das was Prolog macht – nur eben in einer proprietären Sprache. Wenn man Pech hat, kommt auch noch XML ins Spiel die ganze Geschichte wird auch noch schwer lesbar. Wäre es nicht viel smarter, Prolog dafür zu verwenden?
  5. Prolog hat den Anspruch, den Entwickler weitestgehend von Algorithmen zu entlasten und stattdessen nur noch Spezifikationen schreiben zu lassen. Das finde ich einen lohnenswerten Ansatz. Ich hatte das Gefühl, dass ich tatsächlich weniger Algorithmik betreiben muss. Ich muss aber im Gegenzug wissen, wie Prolog die Regeln auswertet – also letztlich welche Algorithmen Prolog anwendet. Und das fand ich nicht immer trivial. Daher hatte ich auch so meine Schwierigkeiten, den Cut-Operator sinnvoll einzusetzen. Aber vielleicht wird das einfacher, mit mehr Übung?
  6. In die gleiche Problemkategorie fällt, dass ich zwischendurch immer mal Situationen hatte, wo eine Regelauswertung auf einmal 20 Sekunden gedauert hat – bei einer handvoll Fakten. Und mir war nicht immer klar, wie das passiert ist. Insgesamt sollen aber Prolog-Programme auch nicht langsamer sein als Programme, die in anderen Programmiersprachen entwickelt werden.
  7. Beeindruckend fand ich, dass ich mein Terminplaner-Beispiel in Prolog mit extrem wenigen Zeilen Code schreiben konnte, ohne dass der Code schwer verständlich wurde. Allerdings hat das Beispiel wieder Benutzungsoberfläche noch Datenbank. Wenn beides dazukommt und man die Lösung mit Rails oder Grails vergleicht, ist der Unterschied vermutlich nicht mehr so drastisch. Schließlich ist SQL ja sowas wie Prolog-Light.
  8. Apropos Benutzungsoberfläche und Datenbank: Prolog ist keine isolierte Regel-Auswertungsmaschine. Anbindungen an Rich-Client-Libs, HTTP und Datenbanken existiert in Form von Libraries.
  9. Durch sein Pattern-Matching ist es mit Prolog ganz einfach, eine internen DSL zu formulieren – auch wenn man sich wünschen würde, die Klammern der Root-Regel noch loswerden zu können.
  10. Regeln haben in Prolog keinen Rückgabewert. Stattdessen werden als Variablen angegebene Parameter zu diesem Zweck verwendet. Das in sehr flexibel und funktioniert eben auch für mehrere Rückgabewerte. Auf der Negativseite steht, dass man für einfache Ausdrücke eigene Variablen einführen muss. Möchte man an eine Regel index+1 übergeben, muss man dazu eine Extra-Variable einführen. Das finde ich definitiv umständlich.
  11. Die meisten Prolog-Systeme bieten ein Modulkonzept. Das ist nicht im ISO-Standard enthalten, aber zwischen die Prolog-Systemen aber sehr ähnlich. Ich habe das Modulsystem nicht benutzt, aber es sieht vernünftig aus.
  12. Außerdem gibt es sowas wie Objektorientierung (mind. in SWI-Prolog), aber das sieht echt merkwürdig aus.
  13. Und dann ist da noch die Datenbasis mit den Regeln und Fakten. Durch diese Datenbasis lassen sich viele Dinge sehr einfach und elegant formulieren. Allerdings stellt sie auch globalen Zustand dar – mit den üblichen Nachteilen globaler Variablen. So muss man z.B. bei Tests selbst dafür sorgen, dass die Datenbasis aufgeräumt wird. Positiv an der Datenbasis ist allerdings, dass nach Programmausführung bzw. Test die Daten für Post-Mortem-Debugging verfügbar sind.

Hat jemand andere oder ergänzende Erfahrungen? Dann immer her damit!

July 27, 2009 at 4:46 pm 1 comment

BDD for Prolog

During my experiments with Prolog I needed a test framework. I startet with the unit test framework written by Ken Egozi. Over time I tried to migrate it to a more BDD like style. I named the result “ProSpec”. Here is the code (Open-Source-License):

it(Spec_Desc/Spec):-
    current_fixture(Fixture),
    retractall(spec_def(Fixture/Spec_Desc/Spec)),
    assert(spec_def(Fixture/Spec_Desc/Spec)). 
 
describe(Fixture) :-
    retractall(spec_def(Fixture/_/_)),
    assert(current_fixture(Fixture)). 
 
end_describe:-
    retractall(current_fixture(_)). 
 
run_one_spec(Pred/Name) :-
	spec_def(Pred/Name/Spec),
	setup_spec,
	call(Spec).

run_specs :-
    dynamic(setup_spec/0),
    dynamic(specs_stats/2),
    write('Run Specs '), nl,
    bagof(Fixture/Specs, bagof((Spec_Desc/Spec), spec_def(Fixture/Spec_Desc/Spec), Specs), SpecsPerPredicate),
    run_specs(SpecsPerPredicate, Passed/Failed),
    write_specs_summary(Passed/Failed). 
 
run_specs(SpecsSpecsPerPredicate, TotalPassed/TotalFailed) :-
    run_specs(SpecsSpecsPerPredicate, 0/0, TotalPassed/TotalFailed). 
 
run_specs([], Passed/Failed, Passed/Failed):-!. 

run_specs([Fixture/Specs|Rest], Passed/Failed, TotalPassed/TotalFailed):-
    nl, write(Fixture), write('...'), nl,
    foreach_spec(Specs, PassedInPredicate/FailedInPredicate),
    write('Passed:'), write(PassedInPredicate),
    (FailedInPredicate > 0, write(' Failed:'), write(FailedInPredicate) ; true),
    nl,
    Passed1 is Passed + PassedInPredicate,
    Failed1 is Failed + FailedInPredicate,
    run_specs(Rest, Passed1/Failed1, TotalPassed/TotalFailed). 
 
foreach_spec(Specs, Passed/Failed):-
    foreach_spec(Specs, 0/0, Passed/Failed). 
 
foreach_spec([], Passed/Failed, Passed/Failed):-!. 
foreach_spec([Spec_Desc/Spec|Rest], Passed/Failed, NewPassed/NewFailed):-
    (
	setup_spec, call(Spec), !,
        NextPassed is Passed + 1,
        NextFailed is Failed,
        write('...'), write(Spec_Desc), write(' (passed)'), nl
    ;
        NextFailed is Failed + 1,
        NextPassed is Passed,
        write('...'), write(Spec_Desc), write(' (FAILED)'), nl
    ),
    foreach_spec(Rest, NextPassed/NextFailed, NewPassed/NewFailed). 
 
write_specs_summary(Passed/0) :- !,
    nl,
    write(Passed), write(' specs passed'),
    nl. 
write_specs_summary(Passed/Failed) :-
    nl,
    write(Passed), write(' specs passed,'), nl,
    write(Failed), write(' specs failed'),
    nl. 
 
reset_all_specs:-
    retractall(spec_def(_/_/_)).

run_spec(Spec) :-
    call(Spec),!,
    specs_passed(X),
    retract(specs_passed(X)),
    NewX is X + 1,
    assert(specs_passed(NewX)).
run_spec(Spec) :-
    failing_specs(X),
    retract(failing_specs(X)),
    NewX = [Spec|X],
    assert(failing_specs(NewX)).

% Asserts
assert_that(Actual, equals:Expected) :-
	Actual \= Expected, nl, write('Expected '), write(Expected), write(' equal to '), write(Actual), nl, fail;
	Actual == Expected.

assert_that(Actual, not_equals:Expected) :-
	Actual == Expected, nl, write('Expected '), write(Expected), write(' not equal to '), write(Actual), nl, fail;
	Actual \= Expected.
	
assert_that(Actual, is_true) :-
	Actual;
	nl, write('Expected '), write(Actual), write(' to be true '), nl, fail.
	
assert_that(Actual, is_false) :-
	Actual, !, nl, write('Expected '), write(Actual), write(' to be false '), nl, fail;
	true.
	
assert_that(Actual, fails) :- 
	assert_that(Actual, is_false).
	
assert_that(Actual, is_empty) :-
	Actual \= [], nl, write('Expected '), write(Actual), write(' to be empty '), nl, fail;
	Actual == [].

assert_that(Actual, has_member:Member) :-
	not(member(Member, Actual)), nl, write('Expected '), write(Actual), write(' to has member '), write(Member), nl, fail;
	member(Member, Actual).

assert_that(Actual, has_no_member:Member) :-
	member(Member, Actual), nl, write('Expected '), write(Actual), write(' to has no member '), write(Member), nl, fail;
	not(member(Member, Actual)).

assert_that(Actual, contains_all:Sublist) :-
	not(contains_all(Actual, Sublist)), nl, write('Expected '), write(Actual), write(' to contain all members of '), write(Sublist), nl, fail;
	contains_all(Actual, Sublist).

contains_all(_, []) :- true.
contains_all(List, [Member]) :- member(Member, List).
contains_all(List, [Member|Rest]) :- member(Member, List), contains_all(List, Rest).

The whole ProSpec can be downloaded from github.

July 21, 2009 at 7:38 pm 1 comment

Prolog-Grundzüge 8: Datenbasis

In den vorigen Blogposts haben wir Fakten, Regeln, das Prolog-System, Pattern-Matching, Rekursion, Fail und den Cut-Operator betrachtet. Damit haben wir die grundlegenden Dinge beisammen, um Prolog-Programme zu schreiben. Jedenfalls fast. Wir müssen noch ein paar Dinge über die Prolog-Datenbank für Fakten und Regeln lernen. Wir können die Datenbasis nämlich zur Programmlaufzeit dynamisch verändern!

Mit assert können wir Fakten und Regeln zur Laufzeit hinzufügen.

assert(benutzer(levi)).

fügt den neuen Fakt “Levi ist ein Benutzer” zur Datenbasis hinzu.

Mit “asserta” wird an den Anfang und mit “assertz” ans Ende der Datenbasis hinzugefügt.

Den Effekt kann man mit “listing” überprüfen:

listing.

listet alle Fakten und Regeln der Datenbasis auf.

Mit “retractall” werden alle Fakten/Regeln aus der Datenbasis entfernt, die zum angegebenen Fakt/Regel passt.

retractall(benutzer(_)).

Anmerkung: Der Unterstrich ist übrigens eine anonyme Variable, die man immer dann einsetzt, wenn man an dem konkreten Wert der Variablen nicht interessiert ist.

Und nicht zuletzt kann man mit “findall” Regeln und Fakten in der Datenbasis suchen:

findall(Benutzer, benutzer(_), BenutzerListe).

liefert alle Regeln namens “benutzer” mit einem Parameter.

July 21, 2009 at 6:12 pm Leave a comment

Prolog-Grundzüge 7: Cut-Operator

Das Prolog-System sucht stets nach allen möglichen Lösungen für eine Anfrage und liefert diese nacheinander als Ergebnis. Manchmal möchte man das aber gar nicht. Stattdessen ist man mit dem erstbesten Ergebnis vollkommen zufrieden.

Nehmen wir ein selbstgebautes IF-THEN-ELSE-Konstrukt als Beispiel. Eine erste Implementation könnte so aussehen:

if_then_else(P, Q, R) :- P, Q ; R.

Wenn die Bedingung Q wahr ist, wird die Then-Regel Q aktiviert. Wenn Q nicht wahr ist, greift die zweite Definition von “if_then_else” und es wird die Else-Regel R aktiviert. (Wir erinnern uns: Komma bedeutet “und”, Semikolon bedeutet “oder”).

Wenn man die if_then_else-Regel so aktiviert, dass die Else-Bedingung aktiviert wird, ist das Ergebnis erwartungskonform.

if_then_else(false, write('then'), write('else')).

gibt “else” aus und hat “true” als Ergebnis.

Anders sieht es aus, wenn wir “true” als Bedingung übergeben.

if_then_else(true, write('then'), write('else')).

schreibt zuerst “then” und hat “true” als Ergebnis. Aber als nächstes wird “else” ausgegeben und wieder “true” geliefert. Nanu? Was ist denn da los?

Die Ursache ist das Auswertungsverfahren, dass Prolog einsetzt. Zuerst wird geprüft, ob P gilt. Wenn das der Fall ist, wird Q ausgewertet. Und danach wird R auch noch ausgewertet – weil nichts dagegen spricht.

Prolog unterscheidet sich hier von dem, was die meisten anderen Programmiersprachen machen. Die meisten Programmiersprachen brechen die Auswertung eines Ausdrucks ab, wenn die weitere Auswertung das Ergebnis nicht mehr ändern kann. Mit dieser Auswertungsregel bräuchte man R nicht mehr auswerten. Die if_then_else-Regel ist durch den ersten Teil der Oder-Klausel bereits “true”.

Um mit dieser Problematik umzugehen, bietet Prolog den Cut-Operator “!”. Der Cut-Operator setzt eine Grenzlinie, über die Prolog bei der Auswertung einer Regel nicht zurück darf (das sogenannte Backtracking wird eingeschränkt). Mit Hilfe des Cut-Operators sieht unsere if_then_else-Regel so aus:

if_then_else(P, Q, R) :- P, !, Q ; R.

und jetzt verhält sich die Regel auch erwartungskonform.

if_then_else(true, write('then'), write('else')).

schreibt zuerst “then” und hat “true” als Ergebnis und mehr passiert nicht.

Anmerkung: Man hätte den Cut-Operator auch noch eine Position weiter nach hinten schreiben können, also hinter Q.

Prinzipiell gibt es zwei Anwendungsbereiche für den Cut-Operator: Logik und Performance. Wir haben den Cut-Operator angewendet, weil unser Programm sonst nicht richtig funktioniert. Manchmal wendet man den Cut-Operator auch an, um aus Performancegründen unnötige Auswertungen zu unterbinden.

Und leider ist der Cut-Operator nicht-trivial. Mit meinen bisherigen Prolog-Kenntnissen kann ich häufig nicht auf Anhieb sagen, ob und wo genau ein Cut-Operator notwendig ist. Ich sehe erst beim Ausprobieren meiner Regeln, dass sie nicht richtig funktionieren – das zeigt sich häufig in Form von Endlosrekursion. Aber vielleicht wird man da mit der Zeit geübter.

Auf jeden Fall muss man für die Anwendung des Cut-Operators ziemlich genau wissen, wie Prolog die Regeln auswertet. Und das finde ich etwas skurril. Schließlich ist Prolog mit dem Anspruch angetreten, dass man sich um die Algorithmik nicht mehr kümmern muss und seine Logik nur noch spezifiziert. Insgesamt hat Prolog da auch Beeindruckendes zu bieten. Aber der Cut-Operator passt da nicht so gut ins Bild. Immerhin habe ich den Cut-Operator in meinen Beispielprogrammen bisher nur selten benötigt.

July 21, 2009 at 5:49 pm 1 comment

Prolog-Grundzüge 6: Fail

Das Prolog-System sucht stets nach allen möglichen Lösungen für eine Anfrage und liefert diese nacheinander als Ergebnis. Nehmen wir einmal an, wir wollen alle Benutzer ausgeben, die wir mit Fakten wie benutzer(stefan). definiert haben. Nehmen wir für unser Beispiel an, wir haben drei Benutzer definiert:

benutzer(stefan).
benutzer(mika).
benutzer(silke).

Dann könnte eine erste Version der Regel zur Ausgabe aller Benutzer so aussehen:

schreibe_alle_benutzer :- benutzer(X), write(X), nl. 

Die Anwendung liefert drei Ergebnisse (in SWI-Prolog “n” nach jedem “true” getippt):

stefan
true;
mika
true;
silke
true

Das ist nicht ganz das, was wir wollen. Wir wollen, dass alle Benutzer auf einmal geschrieben werden. Das können wir dadurch erreichen, dass wir die Regel absichtlich fehlschlagen lassen. Damit zwingen wir das Prolog-System, alle möglichen Belegungen für “X” durchzugehen:

schreibe_alle_benutzer :- benutzer(X), write(X), nl, fail.

Damit erhalten wir:

stefan
mika
silke
false

Das ist schon deutlich besser, aber das “false” am Ende ist etwas störend. Wenn wir “schreibe_alle_benutzer” aus anderen Regeln aufrufen wollen, wird es sogar unangenehm, weil diese Regel immer scheitert. Das Problem lässt sich aber ganz leicht lösen, indem wir das Ergebnis der Regel verodern (; bedeutet in Prolog “oder”):

schreibe_alle_benutzer :- benutzer(X), write(X), nl, fail; true.

Damit erhalten wir:

stefan
mika
silke
true

July 21, 2009 at 5:45 pm Leave a comment

Older Posts