Prolog Course
Prolog Course
1. Introduction to Prolog
o What is Prolog?
o History and Applications
o Difference between Procedural and Declarative Programming
o Basic Structure of Prolog Programs
2. Prolog Syntax and Basics
o Facts, Rules, and Queries
o Atoms, Variables, and Data Types
o Constants and Predicates
o Comments and Formatting in Prolog
3. Unification and Backtracking
o Concept of Unification
o Pattern Matching
o How Prolog Searches: Backtracking
4. Lists and Recursion
o List Basics: Representation and Manipulation
o Recursive Definitions in Prolog
o List Processing (Head and Tail)
5. Arithmetic Operations
o Basic Arithmetic in Prolog
o Comparison Operations
o Using Arithmetic with Queries
6. Control Structures
o Conjunctions, Disjunctions, and Negation
o If-Then-Else in Prolog
o Cut Operator (!): Purpose and Use Cases
7. Input and Output
o Reading and Writing Data
o Interactive Input
o File Handling in Prolog
8. Advanced Data Structures
o Trees, Graphs, and Sets in Prolog
o Representation and Traversal
oPathfinding with Prolog
9. Meta-programming in Prolog
o Working with Logic Programs as Data
o Self-modifying Logic
o Reflective Programming in Prolog
10. Debugging and Optimization
o Tracing and Debugging Prolog Programs
o Performance Optimization Techniques
11. Real-world Applications of Prolog
o Expert Systems
o Natural Language Processing
o Knowledge Representation
12. Project and Assignments
o Mini Projects
o Practical Assignments
o Hands-on Practice with Real-world Problems
1. Introduction to Prolog
What is Prolog?
Prolog was developed in the early 1970s by Alain Colmerauer and Philippe
Roussel in France, with the goal of creating a language suited for natural
language processing (NLP). It was one of the first languages to implement a
form of automatic reasoning and backtracking, making it a powerful tool for
problem-solving.
Over the years, Prolog has found applications in several fields, particularly in
areas that require symbolic reasoning, such as:
int factorial(int n) {
int result = 1;
for (int i = 1; i <= n; i++) {
result *= i;
}
return result;
}
Example in Prolog:
prolog
factorial(0, 1).
factorial(N, F) :- N > 0, N1 is N - 1,
factorial(N1, F1), F is N * F1.
In the above Prolog code, you simply describe the facts (like factorial of 0 is
1) and the rules (how to calculate factorial for other numbers), and Prolog
will deduce the answer when queried.
1. Facts: These are basic assertions about the world, which Prolog stores
in its knowledge base.
o Example: "Ali is the father of Zayd."
prolog
father(ali, zayd).
2. Rules: Rules define relationships and allow Prolog to infer new facts
based on existing facts. A rule consists of a head (the conclusion) and a
body (the conditions that must be true for the head to be true).
o Example: "If X is the father of Y, and Y is the father of Z, then X is
the grandfather of Z."
prolog
3. Queries: A query is how you ask Prolog for information. The system
will attempt to match the query with the facts and rules in its
knowledge base.
o Example: "Who is Zayd's father?"
prolog
?- father(X, zayd).
prolog
?- father(X, Y).
Prolog will return all pairs of X and Y that satisfy the fact or rule about
the father relationship.
This introductory section of Prolog can lay the foundation for understanding
how logic-based programming works. The primary distinction in Prolog lies
in its declarative nature, where the focus is on what needs to be done, and
the system (via backtracking and logical inference) figures out how to
accomplish it.
Facts
A fact in Prolog is a basic assertion about the world that is considered true.
Facts are used to represent static information, like relationships, properties,
or truths about objects. A fact consists of a predicate (a relationship or
property) followed by its arguments.
Syntax:
Example:
Consider the relationship "Ali is the father of Zayd." This can be written as a
fact in Prolog as:
prolog
father(ali, zayd).
You can have multiple facts. For example, you can add more relationships:
prolog
father(ali, zayd).
father(ali, hasan).
father(omar, ali).
The left-hand side (called the "body") of a rule contains conditions that
must be true for the right-hand side (called the "head") to be true.
Syntax:
A rule is written with a head and a body. The head represents the
conclusion, and the body contains conditions that must hold for the
head to be true. The body is separated from the head by :-, which
can be read as "if."
The rule ends with a period (.).
Example:
Consider the rule "X is the grandfather of Z if X is the father of Y and Y is the
father of Z":
prolog
This rule says that X is the grandfather of Z if X is the father of Y and Y is the
father of Z. The :- can be read as "if," and the comma (,) means "and."
prolog
father(ali, zayd).
father(ali, hasan).
father(omar, ali).
Prolog can infer that ali is the grandfather of zayd (through the rule we
just defined) because ali is the father of hasan and hasan is the father
of zayd.
Queries
A query in Prolog is how we ask the system for information based on the
facts and rules in the program. A query is a question that we ask Prolog, and
the system tries to match it with the facts and rules in the knowledge base.
Syntax:
Example:
Let's say we want to find out who Zayd's father is. We can write a query like
this:
prolog
?- father(X, zayd).
This query asks Prolog to find the value of X such that father(X, zayd)
is true. Prolog will search the knowledge base and will return X = ali,
because the fact father(ali, zayd) exists.
prolog
Variables
Example:
The following query uses a variable X to ask who is Zayd’s father:
prolog
?- father(X, zayd).
Here, Prolog will try to match the fact father(ali, zayd) and conclude
that X = ali.
Negation
Example:
If we want to know if Zayd’s father is not Ali, we can write the following
query:
prolog
?- \+ father(ali, zayd).
This query asks Prolog to check if the fact father(ali, zayd) is false. If
it is false, the query will succeed; otherwise, it will fail.
Lists in Prolog
Prolog also supports lists, which are ordered collections of elements. Lists
are written with square brackets [], and elements inside the list are
separated by commas.
Example:
A list of numbers could be written as:
prolog
[1, 2, 3, 4, 5]
prolog
Example:
To check if two variables are equal:
prolog
?- X = Y.
prolog
?- 5 > 3.
Summary
Facts: Basic assertions that are always true.
Rules: Conditional statements that define relationships based on facts.
Queries: Questions that ask the system for information based on facts
and rules.
Variables: Represent unknowns in queries and rules.
Negation: Used to express the opposite of a fact.
Lists: Ordered collections of elements.
Operators: Used to perform operations like equality and arithmetic.
Atoms
An atom in Prolog is the simplest type of term. Atoms are used to represent
constant values, names, or identifiers. They are the most basic form of data
and can be used as facts or predicates. Atoms can be:
Examples:
prolog
father(ali, zayd).
Variables
Examples:
prolog
father(ali, X).
Variables are used to find relationships and infer conclusions. They can
represent any object or value that satisfies a particular condition.
Prolog is a logic programming language, and its data types are quite flexible.
The primary data types in Prolog are:
In Prolog, constants are values that remain the same throughout the
program. Constants can be:
Example:
prolog
age(ali, 30).
Predicates
Example:
prolog
father(ali, zayd).
More Examples:
Example:
prolog
Comments in Prolog
Example:
prolog
2. Multi-line comments: These comments start with /* and end with */.
They can span multiple lines and are useful for longer explanations or
temporarily disabling sections of code.
Example:
prolog
/*
This is a multi-line comment.
It can span several lines and is useful for longer
explanations.
*/
father(ali, zayd).
Formatting Prolog Code
Good formatting is key to writing clear and maintainable Prolog code. Here
are a few tips:
Indentation: Indent the body of rules to show that it is a part of the
rule.
Example:
prolog
grandfather(X, Z) :-
father(X, Y),
father(Y, Z).
Example:
prolog
married(ali, hasan).
father(ali, zayd).
Period (.): Each fact or rule must end with a period (.). It’s important
not to forget this, as Prolog uses it to mark the end of a statement.
Example:
prolog
parent(ali, zayd).
Concept of Unification
Example of Unification:
Consider the following rule:
prolog
prolog
parent(ali, zayd).
Pattern Matching
prolog
parent(ali, zayd).
prolog
parent(ali, X).
Another example:
prolog
parent(ali, X).
prolog
parent(ali, zayd).
parent(ali, hasan).
1. First, X = zayd.
2. Then, Prolog will backtrack and try the next possible solution, X =
hasan.
Backtracking Example
prolog
father(ali, zayd).
father(ali, hasan).
father(omar, ali).
Now, if we query:
prolog
?- grandfather(X, Z).
prolog
parent(ali, zayd).
parent(ali, hasan).
parent(omar, ali).
parent(ali, maryam).
prolog
?- parent(ali, X).
Prolog will:
A list in Prolog is a data structure that can store multiple items, which can be
atoms, numbers, variables, or even other lists. Lists are ordered collections,
meaning the order of the elements matters.
In Prolog, a list is represented using square brackets ([]). The list can be
empty or can contain one or more elements. If the list is not empty, it
consists of a head (the first element) and a tail (the remaining elements of
the list).
Examples:
Empty list: []
Non-empty list: [1, 2, 3] (This is a list with three elements: 1, 2,
and 3)
Another example: [head|[tail]] (This is a list where head is the
first element, and tail is a list itself)
The head is the first element, and the tail is the remaining part of the list. In
Prolog, lists are constructed recursively, meaning that the tail of a list can
itself be another list.
To manipulate lists, Prolog uses two primary concepts: the head and the tail
of a list.
The head of a list is the first element. For example, in the list [1, 2,
3], 1 is the head.
The tail is the remainder of the list after the head. For example, in the
list [1, 2, 3], the tail is [2, 3].
In Prolog, you can extract the head and tail of a list using pattern matching.
Example:
prolog
Here, X will be unified with 1 (the head), and Tail will be unified with [2,
3] (the tail).
Basic List Operations
1. Appending Lists: You can join two lists together using the append/3
predicate.
prolog
prolog
is_empty([]).
is_empty([_|_]) :- fail. % Fail for non-empty lists
5. Head and Tail Extraction: Extracting the head and tail of a list is often
done by matching the list pattern [Head|Tail].
prolog
1. Concatenation of Lists:
prolog
2. Reversing a List:
You can reverse a list in Prolog using recursion. For example:
prolog
reverse([], []).
reverse([Head|Tail], Reversed) :-
reverse(Tail, ReversedTail),
append(ReversedTail, [Head], Reversed).
Recursion in Prolog
What is Recursion?
The length of a list can be found by recursively traversing the list, counting
one element at a time. When the list is empty ([]), the length is 0 (base
case).
prolog
You can also sum the elements of a list by recursively adding the head of the
list to the sum of the tail.
prolog
prolog
Here, Prolog recursively calls reverse/2 to reverse the tail, and then
appends the head at the end of the reversed tail.
Efficiency Considerations
Prolog’s recursive approach is elegant but can be inefficient if not used
carefully, especially for large lists. Some strategies to improve efficiency
include:
Let’s go deeper into recursive definitions in Prolog and how they work, with
plenty of examples.
1. Base Case: The simplest version of the problem, often used to stop the
recursion. It defines what happens when the problem is trivially
solved.
2. Recursive Case: This defines the problem in terms of smaller or
simpler versions of itself. It calls the same rule with a smaller problem,
gradually approaching the base case.
In Prolog, the recursion is usually seen in the way you define rules for lists.
For example, a rule that sums a list recursively would need a base case to
handle the empty list and a recursive case to add the head of the list to the
sum of the tail.
Let’s say we want to define a recursive rule to compute the sum of the
elements in a list. Here's how it works:
prolog
1. Base Case (sum([], 0)): This is the simplest possible list, an empty
list. We define that the sum of an empty list is 0.
2. Recursive Case (sum([Head|Tail], Total)): If the list is not
empty, we break it into the Head (the first element) and the Tail
(the rest of the list). The rule then calls itself with the Tail and adds
the Head to the sum of the Tail.
How It Works:
prolog
[Head|Tail]
When working with lists, Prolog’s pattern matching mechanism allows you
to easily access and manipulate the head and tail.
Let's look at how Prolog processes lists using recursion with examples.
1. Reversing a List
To reverse a list, we recursively move through the list, taking the head and
putting it at the end of the reversed tail.
prolog
The base case says that the reverse of an empty list is itself (an empty
list).
The recursive case first reverses the tail of the list, and then the head
is appended to the reversed tail.
How It Works:
prolog
How It Works:
Appending two lists in Prolog involves recursively traversing the first list and
placing its elements at the beginning of the second list.
prolog
The base case says that appending an empty list to any list results in
the other list.
The recursive case takes the Head of the first list and appends it to the
result of appending the Tail of the first list to the second list.
How It Works:
Let’s explore basic arithmetic operations in Prolog and how you can use
them for solving mathematical problems.
Where:
1. Addition (+):
The + operator is used to add two numbers.
prolog
X is 3 + 5. % X will be 8
2. Subtraction (-):
The - operator is used to subtract one number from another.
prolog
X is 10 - 4. % X will be 6
3. Multiplication (*):
The * operator is used to multiply two numbers.
prolog
X is 4 * 7. % X will be 28
4. Division (/):
The / operator divides the left operand by the right operand and
returns a floating-point result.
prolog
X is 10 / 3. % X will be 3.3333...
X is 10 // 3. % X will be 3
6. Modulus (mod):
The mod operator returns the remainder of a division operation.
prolog
7. Exponentiation (^):
The ^ operator raises a number to the power of another.
prolog
X is 2^3. % X will be 8
prolog
A = 3,
B = 5,
C is A + B. % C will be 8
Let’s say you want to calculate the area of a rectangle given its length and
width. The formula for the area of a rectangle is:
prolog
Now, you can use this rule to calculate the area of a rectangle with length
10 and width 5:
prolog
?- area_of_rectangle(10, 5, Area).
Area = 50.
Example: Solving a Quadratic Equation
ax2+bx+c=0ax^2 + bx + c = 0ax2+bx+c=0
The solutions for this equation can be found using the quadratic formula:
Let’s write a Prolog rule that computes the roots of the quadratic equation:
prolog
Now, you can solve for the roots of the quadratic equation 2x2−4x−6=02x^2
- 4x - 6 = 02x2−4x−6=0:
prolog
Here, the Prolog rule computes the roots of the quadratic equation and
returns X1 = 3 and X2 = -1 as the solutions.
Handling Floating Point Arithmetic
Prolog can handle both integers and floating-point numbers, but there’s a
key difference in how they are processed:
For example:
prolog
X is 10 / 3. % X will be 3.3333...
If you wanted the result as an integer, you would use the // operator
(integer division):
prolog
X is 10 // 3. % X will be 3
+: Addition
-: Subtraction
*: Multiplication
/: Division (floating point result)
//: Integer Division (quotient)
mod: Modulus (remainder)
^: Exponentiation
All arithmetic operations in Prolog require the use of the is/2 operator,
which binds the result of the arithmetic expression to a variable.
Conclusion
Arithmetic operations in Prolog are essential for solving numerical problems,
including basic arithmetic, solving equations, and working with
mathematical formulas. By using the is/2 operator, Prolog allows you to
perform a wide range of calculations and manipulate numbers in a
declarative manner. Whether you are solving simple problems or dealing
with complex equations, Prolog's arithmetic capabilities are robust and
flexible, making it a useful tool for logic-based problem-solving.
prolog
?- 5 =:= 5.
true. % 5 is equal to 5
?- 5 =:= 6.
false. % 5 is not equal to 6
prolog
?- X = 5, X =:= 5.
X = 5. % X is successfully unified with 5, and the
comparison is true
?- X = 5, X =:= 6.
false. % X cannot be both 5 and 6 at the same time
Here, Prolog first unifies the variable X with 5, and then checks if the
comparison X =:= 5 holds true.
Let's write a rule that checks if someone is an adult (age >= 18):
prolog
is_adult(Age) :-
Age >= 18.
prolog
?- is_adult(20).
true.
?- is_adult(16).
false.
You can use the is/2 operator to perform arithmetic and then
compare the result with other values. For instance, if you want to
check if the sum of two numbers equals 10:
prolog
?- X is 3 + 7, X =:= 10.
X = 10.
In this example:
You can also use arithmetic operations in rules. For example, a rule
might calculate the price after a discount and check if the final price is
within a given budget.
prolog
discounted_price(OriginalPrice, Discount,
FinalPrice) :-
FinalPrice is OriginalPrice * (1 - Discount).
Now, you can query whether a product with a given original price and
discount falls within a certain budget:
prolog
In this example:
Let’s say you want to solve a problem where a person’s age is related
to another person’s age. For example, "Sami is 5 years older than Ali."
You can define a Prolog rule that calculates their ages based on a given
age for Ali:
prolog
age_difference(AgeAli, AgeSami) :-
AgeSami is AgeAli + 5.
Now, you can query the age of Sami if Ali’s age is known:
prolog
?- age_difference(25, AgeSami).
AgeSami = 30.
prolog
Now, if you want to check if someone’s salary after a bonus exceeds 5000:
prolog
In this case:
Conclusion
In Prolog, comparison operations and arithmetic operations are essential
for evaluating conditions, making decisions, and solving problems
dynamically. The key comparison operators (=:=, =\=, <, >, =<, >=) allow
you to compare numeric values and variables, while the is/2 operator is
crucial for performing arithmetic calculations.
In this section, we’ll dive deep into each of these control structures and their
usage in Prolog.
Syntax of Conjunction
Example:
Let’s define a rule that checks if someone is eligible to vote, which requires
two conditions:
prolog
is_eligible_to_vote(Age, Citizen) :-
Age >= 18,
Citizen = true.
Now, you can query whether a person with a given age and citizenship is
eligible to vote:
prolog
?- is_eligible_to_vote(20, true).
true.
?- is_eligible_to_vote(16, true).
false.
?- is_eligible_to_vote(20, false).
false.
Explanation:
When Prolog evaluates a conjunction, it will try to satisfy each goal from left
to right. If the first goal fails, Prolog will backtrack to the previous choice
point and attempt to find another solution. If the first goal succeeds but the
second fails, Prolog will also backtrack.
Syntax of Disjunction
Example:
is_teacher_or_student(Person) :-
Person = student;
Person = teacher.
prolog
?- is_teacher_or_student(student).
true.
?- is_teacher_or_student(teacher).
true.
?- is_teacher_or_student(doctor).
false.
Explanation:
Prolog tries each condition in the disjunction, and if the first condition fails,
it automatically tries the next one (backtracking). This behavior is similar to
how OR works in traditional programming languages—if one branch fails, it
will continue evaluating the others.
3. Negation in Prolog
In Prolog, negation is used to represent logical NOT. This means that a goal
is true if the negated condition does not hold.
Syntax of Negation
Example:
Let’s define a rule that checks if a person is not eligible to vote. We will use
negation to express that if a person is not an adult or not a citizen, they are
not eligible to vote.
prolog
is_not_eligible_to_vote(Age, Citizen) :-
\+ (Age >= 18),
\+ (Citizen = true).
Here:
prolog
?- is_not_eligible_to_vote(16, false).
true.
?- is_not_eligible_to_vote(20, true).
false.
?- is_not_eligible_to_vote(17, true).
true.
Explanation:
Negation as Failure
prolog
is_parent_of(john, mary).
is_parent_of(john, tom).
Let’s put all three control structures (conjunction, disjunction, and negation)
together in a more complex example. We want to check whether a person is
eligible to apply for a job based on several conditions:
prolog
prolog
Explanation:
Conclusion
Conjunctions (AND) ensure that all conditions must be true for the
rule to succeed.
Disjunctions (OR) ensure that at least one condition must be true.
Negation (NOT) ensures that the goal does not succeed.
In Prolog, these control structures give you the flexibility to express complex
logical conditions, enabling you to model real-world problems in a
declarative and efficient way.
prolog
Where:
Syntax
prolog
Example:
prolog
check_number(Number) :-
Number > 0 ->
write('Positive');
Number < 0 ->
write('Negative');
write('Zero').
prolog
?- check_number(5).
Positive.
?- check_number(-3).
Negative.
?- check_number(0).
Zero.
Explanation:
You can use this construct when you need to make decisions based on
certain conditions. For example, choosing between different actions based
on whether a person’s age qualifies them for a discount:
prolog
apply_discount(Age, Price) :-
(Age > 60 ->
DiscountedPrice is Price * 0.8 ; % 20% discount
for seniors
Age < 18 ->
DiscountedPrice is Price * 0.9 ; % 10% discount
for minors
DiscountedPrice = Price), % no discount for others
write('Discounted Price: '),
write(DiscountedPrice).
Query:
prolog
?- apply_discount(65, 100).
Discounted Price: 80.
?- apply_discount(15, 100).
Discounted Price: 90.
?- apply_discount(30, 100).
Discounted Price: 100.
In this case:
In simple terms, the cut operator commits Prolog to the current path of
execution and prevents backtracking beyond that point.
When Prolog encounters the cut (!), it commits to the current choice point
and removes all alternatives to that point. If Prolog reaches the cut while
trying to find solutions, it will not backtrack to earlier points and will not
attempt other rules or facts.
Syntax
prolog
goal1, !, goal2.
prolog
check_sign(Number) :-
Number > 0,
!,
write('Positive').
check_sign(Number) :-
Number < 0,
write('Negative').
Here:
If Number > 0 is true, Prolog will execute the first rule and then
commit to this solution by using the cut (!). This means that even if
there are other facts or rules that could be tried, Prolog will not go
back and try them.
If Number < 0 is true, Prolog will execute the second rule (without
backtracking to the first rule due to the cut in the first one).
prolog
?- check_sign(5).
Positive.
?- check_sign(-3).
Negative.
Explanation:
Suppose we have a set of rules that check a person's eligibility for a loan
based on their age and credit score. If the person is under 18 or has a bad
credit score, they are not eligible. After checking the first condition (age), if
the person is ineligible based on age, we don’t need to check their credit
score:
prolog
eligible_for_loan(Age, CreditScore) :-
Age >= 18,
CreditScore >= 700,
!,
write('Eligible for loan').
eligible_for_loan(Age, _) :-
Age < 18,
write('Not eligible (under 18)').
eligible_for_loan(_, CreditScore) :-
CreditScore < 700,
write('Not eligible (poor credit score)').
Here:
If Age >= 18 and CreditScore >= 700, the person is eligible for
a loan, and after printing the message, the cut ensures no other rules
are checked.
If the person is under 18, Prolog immediately concludes they are not
eligible based on age and does not attempt to check their credit score.
Queries:
prolog
?- eligible_for_loan(25, 750).
Eligible for loan.
?- eligible_for_loan(17, 750).
Not eligible (under 18).
?- eligible_for_loan(30, 650).
Not eligible (poor credit score).
Explanation:
In the first query, the age and credit score meet the eligibility criteria,
and the cut prevents further backtracking.
In the second query, the person is under 18, so Prolog doesn’t need to
check the credit score and outputs "Not eligible (under 18)".
In the third query, the credit score is below the threshold, so Prolog
directly outputs "Not eligible (poor credit score)".
Conclusion
If-Then-Else in Prolog allows you to make conditional decisions by
using -> for the "then" part and ; for the "else" part. It provides a way
to execute different actions based on conditions, which is useful for
decision-making in Prolog programs.
The cut operator (!) is a powerful tool to optimize Prolog programs by
preventing backtracking. It commits Prolog to the current choice point
and eliminates the possibility of exploring alternative solutions. The
cut operator is useful for optimizing performance and controlling the
flow of logic in a Prolog program.