linfo2335-programming-parad.../solution.pl

229 lines
8.1 KiB
Prolog
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

:- dynamic tables/0.
/*
Prints the names of all existing tables, one per line (use writeln/1).
A table name is always an atom.
*/
tables :- tabl(X, _), writeln(X).
:- dynamic tables/1.
/*
Unify Tables with a list of the names of all existing tables.
*/
tables(Tables) :- findall(X, tabl(X, _), Tables).
:- dynamic create_table/2.
/*
When this predicate is executed, the effect will be the creation of a new
table with the specified list of column names (order matters!).
A column name is always an atom.
If a table with the given name already exists, the predicate must throw a
descriptive exception (use throw/1).
All exceptions must have a descriptive error message.
*/
create_table(Table, Cols) :-
(tabl(Table, _) -> throw("Table already exists"));
assertz(tabl(Table, Cols)).
:- dynamic cols/2.
/*
Unifies Cols with the list of columns for the specified table (in the same
order as they were supplied to create table/2).
If the given table does not exist, the predicate must throw a descriptive
exception (use throw/1).
*/
cols(Table, Cols) :-
(tabl(Table, _) -> tabl(Table, Cols));
throw("Table doesn't exist").
:- dynamic row/2.
/*
Unifies Row, one result at a time, with each row in the given Table.
If the given table does not exist, the predicate should fail.
*/
row(Table, Row) :-
tabl(Table, Cols) ->
(
length(Cols, L),
length(Row, L),
apply(Table, Row)
); fail.
:- dynamic rows/1.
/*
Displays all rows in the given table, one per line (use writeln/1).
If the given table does not exist, the predicate must throw a descriptive
exception.
*/
rows(Table) :-
\+ tabl(Table, _),
throw(table_does_not_exist(Table)),
!.
rows(Table) :-
row(Table, Row),
writeln(Row),
fail.
rows(_).
:- dynamic insert/2.
/*
When this predicate is executed, the effect will be the addition of a given
row in the given table. The given row is a list of values for each of the
corresponding columns in the table (in the order in which the columns
were supplied to create table/2).
If the given table does not exist, the predicate must throw a descriptive
exception.
If the row does not have as many elements as the number of columns in
the table, the predicate must throw a descriptive exception.
*/
insert(Table, Row) :-
(tabl(Table, Cols) ->
(length(Row, L1), length(Cols, L2), L1 =:= L2 ->
Term =.. [Table | Row],
assertz(Term);
throw("Row doesn't have as many elements as the number of columns in the table")
);
throw("Table doesn't exist")
).
:- dynamic drop/1.
/*
When this predicate is executed, the effect will be the deletion of the given
table.
Do make sure that all of its rows are deleted as well, so that they dont
magically reappear again if you would recreate a table with the same name
and signature later on.
If the given table does not exist, the predicate must throw a descriptive
exception.
*/
drop(Table) :-
(tabl(Table, _) ->
delete(Table), retract(tabl(Table, _));
throw("Table doesn't exist")
).
:- dynamic delete/1.
/*
When this predicate is executed, the effect will be the deletion of all rows
in the given table. The table itself should still exist after, but with no
more rows.
If the given table does not exist, the predicate must throw a descriptive
exception.
*/
delete(Table) :-
(tabl(Table, _) ->
tabl(Table, C), length(C, L), call(abolish, Table, L);
throw("Table doesn't exist")
).
:- dynamic delete/2.
/*
When this predicate is executed, the effect will be the deletion of all rows
from the given table that match all of the given conditions. The table
must still exist after.
If the given table does not exist, the predicate must throw a descriptive
exception.
A condition is any Prolog predicate that could have been typed at the
prompt, but which may include selectors. Selectors are terms of the form
+<column>where <column>should be replaced by a column name.
(See tests.pl for some concrete usage examples.)
*/
delete(Table, Conds) :-
(tabl(Table, _) ->
(rows(tables, Row), \+ (member(Cond, Conds), \+ Cond) -> retract(Row); true);
throw("Table doesn't exist")
).
:- dynamic selec/4.
/*
Note that the name of this predicate is selec (without t) for the simple
reason that select/4 is already a built-in Prolog predicate.
Table is the name of a single table.
Selectors is either * or a list of selectors. These define the resulting
projection. * means: select all column names from the table. Other
selectors explicitly specify which columns to pick. (See above for what
selectors look like.) For example, +name would select the column named
name.
Conds has the same form as in delete/2 and works the same way: only
rows that match all conditions are selected.
Finally, Projection unifies with <selectors>/<projection>, where:
•<selectors>is the list of requested selectors.
•<projection>is a list of values coming from a single row from the
given table that matches the conditions. This mean this predicate
should be able to backtrack to generate all projections that match
the query.
For example, selec(persons,[+id,+first],[],P) returns as first result
P = [+id, +first]/[0, "Jeffrey"].
To obtain all projections that match the query, one could use the Prolog
query findall(X, selec(Table, Selectors, Conds, X), Projections).
For example: findall(X, selec(persons,[+last],[],X), Projections)
returns Projections = [[+last]/["Bowman"],[+last]/["Michaels"],. . . ]
Or if you only want the rows (since the selectors are repeated):
findall(X, selec(Table, Selectors, Conds, /X), Projections).
For example:
findall(Values, selec(persons,[+id,+first],[],Values), Projections)
returns: Projections = [[0, "Jeffrey"], [1, "Lorena"], [2, "Joseph"], ...
*/
/* selec(Table, Selectors, Conds, Projection). */
:- dynamic selec/3.
/*
Simplified variant of the selec/4 predicate when there are no conditions
to be checked.
*/
/* selec(TableOrTables, Selectors, Projection). */
:- dynamic query/2.
/*
where Query is a string whose syntax is defined by the following grammar:
⟨query⟩ ::= ⟨select⟩ | ⟨insert ⟩
⟨select⟩ ::= SELECT ⟨selectors⟩ FROM ⟨table⟩ [⟨where⟩];
⟨selectors⟩ ::= * | ⟨cols⟩
⟨cols⟩ ::= ⟨col ⟩ [, ⟨cols⟩]
⟨where⟩ ::= WHERE ⟨cond ⟩
⟨insert⟩ ::= INSERT INTO ⟨table⟩ [(⟨cols⟩)] VALUES (⟨values⟩);
⟨values⟩ ::= ⟨value⟩ [, ⟨values⟩]
About the notation: the pipe (|) and square brackets ([]) symbols in the
production rules above denote choice and optionality, respectively.
<table>and <col>denote table and column names (respectively). Ta-
ble names are atoms, while column names should follow the format out-
lined before (+<atom>). <cond>denotes a Prolog goal, in Prolog syntax
— following the same format as the possible values of items of the Conds
list passed to selec and delete.
<value>denotes a prolog value that can be stored into a row. You do
not need to handle parsing Prolog (<cond>and <value>) by yourself,
well show how to do it below.
The semantics of this predicate is that of the SQL-predicate contained in
the Query string.
A “SELECT” query maps to the selec predicate, while an “INSERT”
query maps to the insert predicate. For “SELECT”, all instances of
<cond>(appearing in <where>) are mapped to the Conds parameter.
<selectors>map to the Selectors parameter. Tables names (or *) are
mapped to the Table parameter.
For “INSERT”, if the <cols>part is absent, the mapping to insert/2
is straightforward. If <cols>is present, you will have to:
1. reorder the values according to the columns that are present;
2. fill in the missing columns (if any) with a null default values.
Example:
query("INSERT INTO cities (+name, +state)
VALUES (\"Tempa\", \"Florida\");").
maps to (for instance):
insert(cities, ["Tempa", "Florida"]).
When used with “SELECT”:
•query/1 must display the results a bit like the rows predicate would.
You have some flexibility here (displaying the selected column names
is a nice touch for instance).
•query/2 must pass the Result parameter as last parameter to the
selec/4 predicate.
For “INSERT”, nothing needs to be printed by query/1 or in case of
success, and if query/2 is used the Result parameter can be ignored.
*/
/* query(Query, Result). */
:- dynamic query/1.
/*
cf. query/2
*/
/* query(Query). */