3
votes

I am currently trying to figure out how to use the same operator and its related predicates across modules in swi-prolog.

I have a 'main' file (ch20.pl) which I load from the swi-prolog interactive by typing:

[ch20].

ch20.pl currently contains:

:- use_module(ch20_examples).
:- use_module(ch20_Ires).

ch20_examples.pl:

:- module(ch20_examples, [example/2, attribute/2]).

attribute(size, [small, large]).
attribute(shape, [long, compact, other]).
attribute(holes, [none, 1, 2, 3, many]).

example(nut, [size=small, shape=compact, holes=1]).
example(screw, [size=small, shape=long, holes=none]).
example(key, [size=small, shape=long, holes=1]).
example(nut, [size=small, shape=compact, holes=1]).
example(key, [size=large, shape=long, holes=1]).
example(screw, [size=small, shape=compact, holes=none]).
example(nut, [size=small, shape=compact, holes=1]).
example(pen, [size=large, shape=long, holes=none]).
example(scissors, [size=large, shape=long, holes=2]).
example(pen, [size=large, shape=long, holes=none]).
example(scissors, [size=large, shape=other, holes=2]).
example(key, [size=small, shape=other, holes=2]).

ch20_Ires.pl:

:- module(ch20_Ires,
            [   op(900, xfy, <-),
                (<-)/2
            ]).
:- use_module(ch20_utils).

R <- residual_info(Attribute) :-
    findall(X, (example(_, Attributes), member(Attribute=X, Attributes)), Values),
    list_to_set(Values, Values_set),
    setof(Class, Class^Attr^example(Class,Attr), Classes_set),
    Values_prob_sum <- sum_list(Values_set, value_prob(Values, Classes_set, Attribute)),
    R <- -Values_prob_sum, !.

R <- value_prob(Values, Classes_set, Attr, Val) :-
    include(==(Val), Values, Value_count_list),
    Value_count_n <- len(Value_count_list),
    Values_n <- len(Values),
    Val_prob <- Value_count_n / Values_n, 
    R <- Val_prob * sum_list(Classes_set, cond_prob(Attr, Val)), !.

R <- cond_prob(Attr, Val, Class) :-
    findall(_, (example(Class, Class_FeatureList), member(Attr=Val, Class_FeatureList)) , Class_list),
    Class_n <- len(Class_list),
    findall(_, (example(_, FeatureList), member(Attr=Val, FeatureList)), Feature_count_list),
    Feature_count <- len(Feature_count_list),
    (Class_n > 0, Cond_prob <- Class_n / Feature_count, R <- Cond_prob * log2(Cond_prob)
    ;
    R <- 0), !.

ch20_utils.pl:

:- module(ch20_utils,
            [   op(900, xfy, <-),
                (<-)/2
            ]).

R <- log2(X) :- R <- log(X) / log(2), !.
R <- len(L) :- length(L, R), !.
Sum <- sum_list([], _) :- Sum <- 0.
Sum <- sum_list([H|T], Func) :- 
    New_sum <- sum_list(T, Func), 
    !, Func =..Func_list, 
    append(Func_list,[H],Apply_func_list),
    Apply_func =..Apply_func_list,
    Sum <- New_sum + Apply_func.

R <- X :- compound(X), X =..[OP,X2,X3], R2 <- X2, R3 <- X3, Expr =..[OP,R2,R3], R is Expr, !.
R <- X :- R is X, !.

My current goal is to be able to evaluate:

?- X <- residual_info(size).

but it just returns false with the setup above...

Note that I am using the <- operator in an attempt to make the code a little more readable, and to be able to evaluate compounds like:

X <- 18*log2(19)-5.

But this also means that my code is very depended on the part:

R <- X :- compound(X), X =..[OP,X2,X3], R2 <- X2, R3 <- X3, Expr =..[OP,R2,R3], R is Expr, !.
R <- X :- R is X, !.

I have tried messing around with the multifile predicate, but I don't think that I understand how to use it.

However if the ch20_Ires.pl file is changed to:

:- module(ch20_Ires,
            [   op(900, xfy, <-),
                (<-)/2
            ]).
 R <- log2(X) :- R <- log(X) / log(2), !.
 R <- len(L) :- length(L, R), !.
 Sum <- sum_list([], _) :- Sum <- 0.
 Sum <- sum_list([H|T], Func) :- 
     New_sum <- sum_list(T, Func), 
     !, Func =..Func_list, 
     append(Func_list,[H],Apply_func_list), 
     Apply_func =..Apply_func_list, 
     Sum <- New_sum + Apply_func.

R <- residual_info(Attribute) :-
    findall(X, (example(_, Attributes), member(Attribute=X, Attributes)), Values),
    list_to_set(Values, Values_set),
    setof(Class, Class^Attr^example(Class,Attr), Classes_set),
    Values_prob_sum <- sum_list(Values_set, value_prob(Values, Classes_set, Attribute)),
    R <- -Values_prob_sum, !.

R <- value_prob(Values, Classes_set, Attr, Val) :-
    include(==(Val), Values, Value_count_list),
    Value_count_n <- len(Value_count_list),
    Values_n <- len(Values),
    Val_prob <- Value_count_n / Values_n,
    R <- Val_prob * sum_list(Classes_set, cond_prob(Attr, Val)), !.

R <- cond_prob(Attr, Val, Class) :-
    findall(_, (example(Class, Class_FeatureList), member(Attr=Val, Class_FeatureList)) , Class_list),
    Class_n <- len(Class_list),
    findall(_, (example(_, FeatureList), member(Attr=Val, FeatureList)), Feature_count_list),
    Feature_count <- len(Feature_count_list),
    (Class_n > 0, Cond_prob <- Class_n / Feature_count, R <- Cond_prob * log2(Cond_prob)
    ;
    R <- 0), !.

R <- X :- compound(X), X =..[OP,X2,X3], R2 <- X2, R3 <- X3, Expr =..[OP,R2,R3], R is Expr, !.
R <- X :- R is X, !.

everything works fine and I can execute my goal:

?- X <- residual_info(size).
X = 1.5421864522230475.

but I really want to separate my logic into more modules, instead of having everything in one file. And I want to be able to use this public/private-like system which modules are supposed to provide.

Are there anything I can rearrange in the code, or can I maybe solve my problem somehow with this multifile predicate?

And on a side note, do there exist any books that teaches stuff like using modules and other advanced prolog techniques? because I find it very hard to use the swi-prolog documentation and it is hard to find examples with google..

1
You can (alternatively) declare the (<-)/2 predicate as a multifile predicate living in the user pseudo-module and with your other modules contributing with clauses for this multifile predicate. Simply adapt Carlo's answer to use :- multifile user:(<-)/2.. But one (encapsulation) advantage of his original solution is that the (<-)/2 predicate is only visible in user unqualified if you load a module (into user) that exports it.Paulo Moura
try www SymbolHound dot com for searches with symbols.Will Ness

1 Answers

3
votes

Modules are one of the most controversial issues in Prolog standardization. Then it's hard to find coherent information on the topic and examples.

IMHO your problem derives from too much expectation about the construct, that does something simple, but has complicated consequences, because it changes one fundamental property of the language element: atoms, when used as predicate identifiers, become pairs Module:Name, where Module is called the context (well, kind of... I hope someone will contribute to understand this important topic with more precise wording and references).


To simplify a bit your problem, let's rename (<-)/2 as eval/2. Then

:- module(ch20_Ires, []).
:- reexport([ch20_utils, ch20_examples]).

ch20_utils:eval(R, residual_info(Attribute)) :-
    findall(X, (example(_, Attributes), member(Attribute=X, Attributes)), Values),
    list_to_set(Values, Values_set),
    setof(Class, Class^Attr^example(Class,Attr), Classes_set),
    eval(Values_prob_sum, sum_list(Values_set, value_prob(Values, Classes_set, Attribute))),
    eval(R, -Values_prob_sum), !.
...    
% note: moved here from ch20_utils
ch20_utils:eval(R, X) :- R is X, !.

and

:- module(ch20_utils, [ eval/2 ]).
:- multifile eval/2.

eval(R, log2(X)) :- eval(R, log(X) / log(2)), !.
...

we get the expected result from topline

17 ?- eval(X, residual_info(size)).
X = 1.5421864522230475.

Note the declaration in ch20_utils (where eval is first seen):

:- multifile eval/2.

that allows to continue the definitions in some other module, but requires to qualify further definitions with ch20_utils identifier. Then the recursive calls will have chances find those definitions.

Note I moved the last clause from ch20_utils to ch20_Ires. This is required because that clause is a 'catchall' rule, thus inhibited inspection of ch20_Ires clauses: actually, I got

12 ?- eval(X, residual_info(size)).
ERROR: is/2: Arithmetic: `size/0' is not a function

before moving the clause...


Now, back to original problem, as you know operators are just syntax sugar:

:- module(ch20_Ires, []).
:- reexport([ch20_utils, ch20_examples]).

ch20_utils:(R <- residual_info(Attribute)) :-
    findall(X, (example(_, Attributes), member(Attribute=X, Attributes)), Values),
    list_to_set(Values, Values_set),
    setof(Class, Class^Attr^example(Class,Attr), Classes_set),
    Values_prob_sum <- sum_list(Values_set, value_prob(Values, Classes_set, Attribute)),
    R <- -Values_prob_sum, !.

...

% note: moved here from ch20_utils
ch20_utils:(R <- X) :- R is X, !.

and

:- module(ch20_utils,
            [ op(900, xfy, <-),
                (<-)/2
        ]).

:- multifile (<-)/2.

R <- log2(X) :- R <- log(X) / log(2), !.
R <- len(L) :- length(L, R), !.
Sum <- sum_list([], _) :- Sum <- 0.
Sum <- sum_list([H|T], Func) :-
    New_sum <- sum_list(T, Func),
    !, Func =..Func_list,
    append(Func_list,[H],Apply_func_list),
    Apply_func =..Apply_func_list,
    Sum <- New_sum + Apply_func.

R <- X :- compound(X), X =..[OP,X2,X3], R2 <- X2, R3 <- X3, Expr =.. [OP,R2,R3], R is Expr, !.

% R <- X :- R is X, !.

seem to solve the syntax issue:

20 ?- X <- residual_info(size).
X = 1.5421864522230475.