Prolog (Programming in Logic) is one of the oldest and best-known declarative programming languages. It was created in the 1970s by Alain Colmeraur and Phillip Rousselot. It is a language in which the programmer describes a problem in terms of facts, rules and queries, and the computer system independently draws conclusions and searches for solutions.
Prolog is widely used in areas that require solving logical problems, such as:
This is a fact in Prolog that describes the 'parent' relationship. In this case:
rodzic(jozef,jacek) oznacza, że Józef jest rodzicem Jacka.
Facts in Prolog are basic statements that are assumed to be true. Each fact consists of a predicate (e.g. parent) and arguments (e.g. jozef and jacek), which are the data associated with that predicate.
In the Prologue \+ stands for negation. It is an operator that checks whether an expression is false. You can understand it as asking „Is this not true?”. The operator \+ works like a logical negation in other programming languages.
An example of the use of negation:
\+ rodzic(jozef, jacek).
This query checks if jozef is not the parent of jacek. If the fact parent(jozef, jacek) is not stored in the database, the result will be true (because negation of a false statement yields true). If such a fact exists, the result will be false.
Negation in Prolog works as follows:
\+ A will be true if A is false.\+ A A will be false if A is true.Examples:
parent(jozef, jacek), the query \+ ■ + parent(jozef, jacek). will return false.\+ parent(krzysztof, jacek). (which is not stored as a fact in the database), it will return true.Predicates and rules:
% fakty małżeństwo(jacek,iza). małżeństwo(andrzej,anna). małżeństwo(jan,krystyna). małżeństwo(jozef,halina). małżeństwo(cezary,cecylia). małżeństwo(henryk,hanna). małżeństwo(darek,dorota). % dzieci(jacka i iza) rodzic(jacek,krzys). rodzic(iza,krzys). rodzic(jacek,ola). rodzic(iza,ola). rodzic(iza,julek). %dzieci anrzej i anna rodzic(andrzej,jas). rodzic(anna,jas). % dzieci jana i krystyny rodzic(krystyna,iza). rodzic(krystyna,jagoda). rodzic(krystyna,andrzej). rodzic(krystyna,jurek). rodzic(jan,iza). rodzic(jan,jagoda). rodzic(jan,andrzej). rodzic(jan,jurek). % dzieci cezary i cecylia rodzic(cecylia,halina). rodzic(cezary,halina). % dzieci dorota i darek rodzic(dorota,danuta). rodzic(dorota,nadzieja). rodzic(darek,danuta). rodzic(jacek,nadzieja). % dzieci jozefa i haliny rodzic(halina,jacek). rodzic(halina,hanna). rodzic(halina,piotrek). rodzic(jozef,jacek). rodzic(jozef,hanna). rodzic(jozef,piotrek). rodzic(adam,julek). kobieta(iza). kobieta(jagoda). kobieta(ola). kobieta(krystyna). kobieta(halina). kobieta(hanna). kobieta(cecylia). kobieta(dorota). kobieta(anna). kobieta(nadzieja). %reguły mężczyzna(X) :- \+ kobieta(X). ojciec(X,Y) :- rodzic(X,Y), mężczyzna(X). matka(X,Y):- rodzic(X,Y), kobieta(X). dziecko(X,Y) :- rodzic(Y,X). wnuk(X,Y) :- dziecko(D,Y), dziecko(X,D). rodzeństwo_n(X,Y) :- matka(M,Y), matka(M,X), ojciec(O,Y), ojciec(O,X), X \= Y. rodzeństwo_p(X,Y) :- matka(M,Y), matka(M,X), ojciec(O1,Y), ojciec(O2,X), X \= Y, O1 \= O2. rodzeństwo_p(X,Y) :- matka(M1,Y), matka(M2,X), ojciec(O,Y), ojciec(O,X), X \= Y, M1 \= M2. rodzeństwo(X,Y) :- rodzeństwo_n(X,Y); rodzeństwo_p(X,Y). siostra(X,Y) :- rodzeństwo(X,Y), kobieta(X). brat(X,Y) :- rodzeńśtwo(X,Y), mężczyzna(X). mąż(X,Y) :- mężczyzna(Y), małżeństwo(X,Y), kobieta(Y). żona(X,Y) :- kobieta(X), małżeństwo(X,Y), mężczyzna(Y).
Example queries:
% Zapytania do modelu Prolog % Komentarze wyjaśniające, co każde zapytanie robi % Pytanie 1: Sprawdzamy, czy Jacek i Iza są małżeństwem. % Zapytanie sprawdza fakt w bazie danych małżeństwo(jacek, iza). % Oczekiwana odpowiedź: tak (True) % Pytanie 2: Sprawdzamy, kto jest ojcem Krzysia. % Zapytanie testuje regułę "ojciec" ojciec(X, krzys). % Oczekiwana odpowiedź: X = jacek % Pytanie 3: Sprawdzamy, kto jest matką Oli. % Zapytanie testuje regułę "matka" matka(X, ola). % Oczekiwana odpowiedź: X = iza % Pytanie 4: Kto jest dzieckiem Jacka? % Zapytanie testuje regułę "dziecko" dziecko(X, jacek). % Oczekiwana odpowiedź: X = krzys ; X = ola ; X = nadzieja % Pytanie 5: Sprawdzamy, czy Krzysiu i Ola to rodzeństwo. % Zapytanie testuje regułę "rodzeństwo_n" (rodzeństwo na podstawie tych samych rodziców) rodzeństwo_n(krzys, ola). % Oczekiwana odpowiedź: tak (True) % Pytanie 6: Kto jest wnukiem Jana? % Zapytanie testuje regułę "wnuk" wnuk(X, jan). % Oczekiwana odpowiedź: X = iza ; X = jagoda ; X = andrzej ; X = jurek % Pytanie 7: Sprawdzamy, czy Iza i Jagoda są siostrami. % Zapytanie testuje regułę "siostra" siostra(iza, jagoda). % Oczekiwana odpowiedź: tak (True) % Pytanie 8: Kto jest mężem Anny? % Zapytanie testuje regułę "mąż" mąż(X, anna). % Oczekiwana odpowiedź: X = andrzej % Pytanie 9: Kto jest żoną Jana? % Zapytanie testuje regułę "żona" żona(X, jan). % Oczekiwana odpowiedź: X = krystyna % Pytanie 10: Kto jest bratem Izy? % Zapytanie testuje regułę "brat" brat(X, iza). % Oczekiwana odpowiedź: X = andrzej ; X = jurek % Pytanie 11: Kto jest ojcem Jasem? % Zapytanie testuje regułę "ojciec" ojciec(X, jas). % Oczekiwana odpowiedź: X = andrzej % Pytanie 12: Sprawdzamy, czy Jacek i Halina są małżeństwem. % Zapytanie testuje fakt w bazie danych małżeństwo(jacek, halina). % Oczekiwana odpowiedź: nie (False) % Pytanie 13: Kto jest ojcem Jagody? % Zapytanie testuje regułę "ojciec" ojciec(X, jagoda). % Oczekiwana odpowiedź: X = jan % Pytanie 14: Kto jest matką Krystyny? % Zapytanie testuje regułę "matka" matka(X, krystyna). % Oczekiwana odpowiedź: brak odpowiedzi, ponieważ nie mamy takiego faktu % Pytanie 15: Kto jest rodzeństwem Haliny? % Zapytanie testuje regułę "rodzeństwo" rodzeństwo(X, halina). % Oczekiwana odpowiedź: X = cezary ; X = cecylia % Pytanie 16: Kto jest siostrą Izy? % Zapytanie testuje regułę "siostra" siostra(X, iza). % Oczekiwana odpowiedź: X = jagoda
In the Prologue \= denotes an inequality.
This is a comparison operator that checks whether two values (or variables) are different.
In this case:
X \= O means that X is different from O.
In the Prologue _ is a so-called anonymous variable. This means that we are not interested in the value of this variable and will not use it in the rest of the program. Prolog accepts it, but does not assign any specific value to it.
In Prolog you can use _, when you do not care about the results of this variable, e.g. in the case of:
motyw(X, zazdrość) :- kobieta(X), zamordowana(O), romans(O, M), romans(X, M), X \= O.
In the case above, the variable M in the romance(O, M) rule is used because we are checking the romance between O and M, but if we otherwise do not want to use a variable, we write it as _:
romans(_, _). % przykładowy zapis, który oznacza, że nie zależy nam na wartościach
This tells Prolog: „Accept all possible values, but we will not use or check them”.
So _ acts as a variable whose value we will not use in further logic.
Predicates and rules:
% Fakty osoba(tomasz, 55, stolarz). osoba(krzysztof, 25, piłkarz). osoba(krzysztof, 25, rzeźnik). osoba(piotr, 25, złodziej). osoba(anna, 39, chirurg). romans(anna, piotr). romans(anna, krzysztof). romans(agnieszka, piotr). romans(agnieszka, tomasz). zamordowana(agnieszka). prawdopodobnie_zamordowana(agnieszka, kij_golfowy). prawdopodobnie_zamordowana(agnieszka, łom). pobrudzony(tomasz, krew). pobrudzony(agnieszka, krew). pobrudzony(krzysztof, krew). pobrudzony(krzysztof, błoto). pobrudzony(piotr, błoto). pobrudzony(anna, krew). posiada(tomasz, sztuczna_noga). posiada(piotr, rewolwer). podobne_obrażenia(sztuczna_noga, kij_golfowy). podobne_obrażenia(noga_od_stołu, kij_golfowy). podobne_obrażenia(łom, kij_golfowy). podobne_obrażenia(nożyczki, nóż). podobne_obrażenia(but_piłkarski, kij_golfowy). % Fakty o płci mężczyzna(piotr). mężczyzna(krzysztof). mężczyzna(tomasz). kobieta(anna). kobieta(agnieszka). % Reguły posiada(X, but_piłkarski) :- osoba(X, _, piłkarz). posiada(X, piłka) :- osoba(X, _, piłkarz). posiada(X, nóż) :- osoba(X, _, rzeźnik). posiada(X, nóż) :- osoba(X, _, chirurg). posiada(X, nożyczki) :- osoba(X, _, chirurg). posiada(X, łom) :- osoba(X, _, złodziej). posiada(X, noga_od_stołu) :- osoba(X, _, stolarz). posiada(X, narzędzie_zbrodni) :- posiada(X, rewolwer); posiada(X, nóż); posiada(X, kij_golfowy); posiada(X, nożyczki); posiada(X, but_piłkarski); posiada(X, noga_od_stołu); posiada(X, sztuczna_noga); posiada(X, łom). podejrzany(X) :- zamordowana(O), prawdopodobnie_zamordowana(O, Y), podobne_obrażenia(N, Y), posiada(X, N). motyw(X, zazdrość) :- mężczyzna(X), zamordowana(O), romans(O, X). motyw(X, zazdrość) :- kobieta(X), zamordowana(O), romans(O, M), romans(X, M), X \= O. motyw(X, pieniądze) :- mężczyzna(X), osoba(X, _, złodziej). morderca(X) :- podejrzany(X), zamordowana(O), motyw(X, _), pobrudzony(O, S), pobrudzony(X, S). motyw_mordercy(M) :- morderca(X), motyw(X, M).
Answer the questions:
Who owns the tools of the crime ?
owns(X, tool_of_crime).
Who is suspected of the murder?
suspect(X).
What motives did the individuals have for the crime?
Motive(X, M).
Who is the murderer?
murderer(X).
What motive did the murderer have?
motive_murderer(M).
List structures in the Prolog are the primary mechanism for representing data sets. Lists are recursive structures, which allows flexible processing.
A list in Prolog is an ordered collection of elements, written in square brackets and separated by commas:
[el1, el2, el3]
A list can also be empty:
[]
A list can be broken down into a head (the first element) and a tail (the rest of the list):
[H|T]
Example:
?- [H|T] = [1,2,3]. H = 1, T = [2,3].
The `member/2` operator checks whether an element belongs to a list:
member(X, [a,b,c]).
The `append/3` operator is used to merge two lists:
append([1,2], [3,4], Result). Result = [1,2,3,4].
The predicate `length/2` is used to specify the length of a list:
length([a,b,c], N). N = 3.
Due to the recursive nature of lists, most algorithms that operate on lists use recursion.
An example of summing the elements of a list:
sum([], 0). sum([H|T], S) :- sum(T, S1), S is H + S1.
Lists can contain variables, other lists, as well as complex structures:
[[notatki:a_b_c_d]]
They can also be open-ended lists:
[1,2|X]
This is useful when constructing dynamic lists.
A predicate that prints out each list element on a separate line:
print_list([]). print_list([H|T]) :- write(H), nl, print_list(T).
Example of use:
?- print_list([apple, banana, cherry]). apple banana cherry
To obtain the first element of a list, we can use pattern matching using the operator |.
Example predicate:
first_element([H|_], H).
Example of use:
?- first_element([a, b, c], X). X = a.
To obtain the second element of the list, we can use pattern matching, bypassing the first element.
Predicate:
second_element([_, Second|_], Second).
Example of use:
?- second_element([x, y, z], X). X = y.
To extract the last element of a list, we can use recursive pattern matching.
Predicate:
last_element([X], X). last_element([_|T], X) :- last_element(T, X).
Example of use:
?- last_element([a, b, c, d], X). X = d.
To check whether an element is in a list, you can use the built-in `member/2` predicate, or define your own version.
Recursion-based version:
in_list([X|_], X). in_list([_|T], X) :- in_list(T, X).
Usage example:
?- in_list([1, 2, 3, 4], 3). true. ?- in_list([a, b, c], d). false.
Alternative: using the embedded predicate `member/2`:
?- member(3, [1,2,3,4]). true.
A predicate that checks whether the elements of a numeric list are increasing or equal (not decreasing):
sorted_asc([]). sorted_asc([_]). sorted_asc([X, Y | T]) :- X =< Y, sorted_asc([Y | T]).
Example of use:
?- sorted_asc([1, 2, 2, 4, 5]). true. ?- sorted_asc([1, 3, 2, 4]). false.
Predicate that inserts an element `X` into a sorted ascending list `List`, returning a new list `Result`, also ordered ascending:
insert_sorted(X, [], [X]). insert_sorted(X, [H|T], [X,H|T]) :- X =< H. insert_sorted(X, [H|T], [H|R]) :- X > H, insert_sorted(X, T, R).
Usage example:
?- insert_sorted(3, [1, 2, 4, 5], Result). Result = [1, 2, 3, 4, 5].
Predicate that sorts a numerical list ascendingly using the insertion sort algorithm (*insertion sort*).
Definition:
insert_sorted(X, [], [X]). insert_sorted(X, [H|T], [X,H|T]) :- X =< H. insert_sorted(X, [H|T], [H|R]) :- X > H, insert_sorted(X, T, R). insertion_sort([], []). insertion_sort([H|T], Sorted) :- insertion_sort(T, SortedT), insert_sorted(H, SortedT, Sorted).
Action description:
Usage example:
?- insertion_sort([4, 1, 3, 2], Sorted). Sorted = [1, 2, 3, 4].
The predicate `length_list/2` calculates the length of a list - that is, the number of its elements - using recursion.
Definition:
length_list([], 0). length_list([_|T], N) :- length_list(T, N1), N is N1 + 1.
Usage example:
?- length_list([a, b, c, d], N). N = 4.
Alternatively, the built-in predicate `length/2` can be used:
?- length([a, b, c, d], N). N = 4.
The predicate `sum_list/2` calculates the sum of all numeric elements contained in a list.
Definition:
sum_list([], 0). sum_list([H|T], Sum) :- sum_list(T, Rest), Sum is H + Rest.
Example of use:
?- sum_list([1, 2, 3, 4], S). S = 10.
You can also use the built-in predicate `sum_list/2`, which works identically:
?- sum_list([1,2,3,4], S). S = 10.
The predicate `average_list/2` calculates the arithmetic mean of all numeric elements in a list.
Requires two auxiliary predicates:
Definition:
sum_list([], 0). sum_list([H|T], Sum) :- sum_list(T, Rest), Sum is H + Rest. length_list([], 0). length_list([_|T], N) :- length_list(T, N1), N is N1 + 1. average_list(List, Avg) :- sum_list(List, Sum), length_list(List, Length), Length > 0, Avg is Sum / Length.
Action description:
Example of use:
?- average_list([2, 4, 6, 8], A). A = 5.0.
Note: the predicate checks that the length of the list is greater than zero to avoid dividing by zero.