0

I have a knowledge base that consists of students database in a file 'students.pl' like this:

% student(Name,Percent,List_of_Marks_in_3_subjects).
student('abc',83,[80,80,90]).
student('pqr',70,[70,60,80]).
student('xyz',76,[80,70,80]).

I want to access each student predicate from the knowledge base and calculate the average marks in each subject or average percentage, without using 'findall' or assert/retract. I may want to use backtracking like this:

find_score_all(X) :- student(Name,Percent,L),
    write(Percent),nl,
    fail.
find_score_all(_).

With this approach I can access each element and write it, but if I want to add each 'Percent' value as an element to a list or just use a predicate like 'Percent1 is Total + Percent' to total the percent values and then find its average, how can I do so? Note that I dont want to use findall or retract/assert and preferably find the average in one pass through the knowledge base since the knowledge base is very large.

Any help is appreciated.

shujin
  • 181
  • 12
  • You're describing a failure-driven loop, but such loops cannot pass information from iteration to iteration except through assert/retract (unless you use an extension, such as SWI's global variable library). Why are you prohibited from using standard practices? – Daniel Lyons Feb 19 '13 at 04:10
  • Can you add numeric id from 1 to N to every student fact in the database? – Sergii Dymchenko Feb 19 '13 at 07:23
  • @j4n-bur53 That link answer uses assert. This question states in the topic not to use findall or assert. – shujin Jul 01 '16 at 11:07
  • @j4n-bur53 That question [link] (http://stackoverflow.com/questions/7647758/prolog-findall-implementation) answes uses search and makes a pass through the whole knowledge base for every search, which is different from this case. – shujin Jul 01 '16 at 17:43
  • Maybe you are expecting a thread solution. http://stackoverflow.com/a/38152802/502187 It is to expect that in future Prolog systems implementations thease threads will become more widespread adopted and also cheaper concerning speed and memory. –  Jul 01 '16 at 19:36

2 Answers2

0
%solution for sum of percents, you can replace with any other calculation sum_percent predicate.
listing(student/3, convert_to_list/2, sum_percent, sum_percent/2).

% student(Name,Percent,List_of_Marks_in_3_subjects).
student('abc',83,[80,80,90]).
student('pqr',70,[70,60,80]).
student('xyz',76,[80,70,80]).

convert_to_list(X, R):-
    student(N, P, LM),
    not(member(st(N, P, LM), X)),
    convert_to_list([st(N, P, LM)|X], R).

convert_to_list(X, X).

sum_percent:-
    convert_to_list([], X),
    sum_percent(X, S),
    write(S).

sum_percent([], 0).
sum_percent([st(_,E,_)|T], S):-
    sum_percent(T, S2),
    S is E+S2.
-1

if you want to add to a list then you should use findall, or better, library(aggregate). But if you fear about efficiency, you could use something like this

integrate(ave, Goal, Ave) :-
    State = state(0, 0, _),
    repeat,
    (   call(Goal, V),
        arg(1, State, C), U is C+1, nb_setarg(1, State, U),
        arg(2, State, S), T is S+V, nb_setarg(2, State, T),
        fail
    ;   arg(1, State, C), arg(2, State, S), Ave is S/C
    ).

:- meta_predicate integrate(+, :, ?).

test:

members(X) :- member(X, [1,2,3,4]).

?- integrate(ave, members, R).
R = 2.5 .

Of course, you'll need to add error handling (at least, when counter C == 0).

CapelliC
  • 59,646
  • 5
  • 47
  • 90