How to get a rule to check all the conditions befo

2019-08-17 08:14发布

问题:

I'm trying to write a predicate weird_sum(List, Result) which takes a List of numbers, and computes the sum of the squares of the numbers in the list that are greater than or equal to 5, minus the sum of the absolute values of the numbers that are less than or equal to 2. For example:

?- weird_sum([3,6,2,-1], Result).
Result = 33 

That's 6×6 - 2 - 1.

I'm trying to check using two conditions whether the number is >= 5 or <= 2. But whenever I call the predicate "computerResult" it will only check the first condition and exit. It never reaches the 2nd condition. How do I make it check the 2nd condition if it fails the first condition?

Here is my code:

%base case
weird_sum([], 0).       

%figuring out if number is large or small
computeResult(Head, Result + Head*Head):-
    Head >= 5.

computeResult(Head, Result - abs(Head)):-
    Head @=< 2.

%recursive case
weird_sum([Head|Tail], Result):-
    weird_sum(Tail, Result),
    computeResult(Head,Result).

回答1:

The problem is not that it does not try the second clause. The problem is the second parameter in the computeResult/2 predicate. You write Result + Head*Head, etc. so that means that Prolog will try to unify a number (0) with something of the form +(Result,*(Head,Head)) and this obviously fails.

You can however easily modify the result like:

%base case
weird_sum([], 0).
%recursive case
weird_sum([Head|Tail], Result) :-
    weird_sum(Tail, SubResult),
    computeResult(Head,Adder),
    Result is SubResult+Adder.   

%figuring out if number is large or small
computeResult(Head, Head*Head):-
    Head >= 5.
computeResult(Head, -abs(Head)):-
    Head =< 2.
computeResult(Head, 0) :-
    Head < 5,
    Head > 2.

You can also add cuts (!) to the conditions to prevent backtracking over the computeResult/2 clauses. In that case you can omit the checks in the last clause.

%figuring out if number is large or small
computeResult(Head, Head*Head):-
    Head >= 5,
    !.
computeResult(Head, -abs(Head)):-
    Head =< 2,
    !.
computeResult(_, 0).

Nevertheless it is more efficient to use an accumulator, like:

weird_sum(List, Result) :-
    weird_sum(List, 0, Result).

weird_sum([], Result, Result).
weird_sum([Head|Tail], SubResult, Result) :-
    computeResult(Head,Adder),
    SubResult2 is SubResult+Adder,
    weird_sum(Tail, SubResult2, Result).


标签: prolog