Brian Pattison
Brian Pattison

Reputation: 21

How to get full stop in this Prolog predicate?

I have this following code:

factor_in_inches(Unit, Scale) :- recursiveScale(Unit, Scale, inch).

%Part3
scale_factor(Unit1, Unit2, Factor) :- recursiveScale(Unit2, Factor, Unit1).

%Part 1 - wrote alternate scale function so I could keep part instead of deleting for part 2 
findScale(Unit1, Scale, Unit2) :-
    scale(Unit1, Scale, Unit2);
    scale(Unit2, Scale1, Unit1),
    Scale is float(1/Scale1).

%need to flip scales because not all units have direct conversions to eachother
%have to find reciprocal conversion and then flip it

%Part2
%have to use findScale instead of regular scale because otherwise
%the recursive fucntion did not work for direct conversions to inches
%had to use findScale to get base conversions for inches
recursiveScale(Big, Scale, Smallest) :-
    findScale(Big, Scale, Smallest);
    recursiveScale(Smaller, MoreScale, Smallest),
    findScale(Big, ScaleAgain, Smaller),
    Scale is float(ScaleAgain * MoreScale).


%Part 4
convert(Unit1, Quantity1, Unit2, Quantity2) :-
    factor_in_inches(Unit1, Factor1),
    factor_in_inches(Unit2, Factor2),
    Factor1 is round((Factor2 * Quantity2)/ Quantity1).

And it is based on these facts:

scale(foot,    12, inch).
scale(yard,     3, foot).
scale(rod,    198, inch).
scale(chain,   22, yard).
scale(furlong, 40, rod).
scale(mile,     8, furlong).
scale(league,   3, mile).

I am trying to run the following tests cases to test my "convert" predicate but on the third test it gets stuck on an infinite loop.

test(convert) :- convert(foot, 2,  inch,  24.0).
test(convert) :- convert(foot, 6,  yard,   2.0).
test(convert) :- convert(chain, 2, foot, 132.0).

How would I go about stopping the backtracking so this last test would not run infinitely?

Thank you.

Upvotes: 1

Views: 141

Answers (3)

CapelliC
CapelliC

Reputation: 60014

Seems it could be a lot simpler:

factor_in_inches(inch, 1) :- !.
factor_in_inches(Unit, Scale) :-
    scale(Unit, Scale1, Down),
    factor_in_inches(Down, Scale2),
    Scale is Scale1 * Scale2.

edit

The cut is there for efficiency, removing it - i.e. the first clause become

factor_in_inches(inch, 1).

you can enumerate all factors:

?- factor_in_inches(U,S).
U = inch,
S = 1 ;
U = foot,
S = 12 ;
U = yard,
S = 36 ;
U = rod,
S = 198 ;
U = chain,
S = 792 ;
U = furlong,
S = 7920 ;
U = mile,
S = 63360 ;
U = league,
S = 190080 ;
false.

Upvotes: 1

David Tonhofer
David Tonhofer

Reputation: 15316

There is no reason in this problem for anything to run indefinitely.

You would just scale-down a value in some original denomination to "inches". Then scale-up the "inches" value to the target denomination.

scaling order

The findScale/3 predicate already looks fishy. You want to scale-down, as in findScale(Big, Scale, Smallest). However, the third line tries to find scale(Unit2, Scale1, Unit1) where Unit2 is a larger scale than Unit1. No such fact exists in the database.

findScale(Unit1, Scale, Unit2) :-
    scale(Unit1, Scale, Unit2);
    scale(Unit2, Scale1, Unit1),
    Scale is float(1/Scale1).

What you want is a predicate

conversionFactor(U1,U2,F) :- larger(U1,U2),
                             stepDown(U1,Ux,Fx),
                             conversionFactor(Ux,U2,Fxx),
                             F is Fx*Fxx.
conversionFactor(U1,U2,F) :- larger(U2,U1),
                             conversionFactor(U2,U1,Fx),
                             F is 1.0/Fx.
conversionFactor(U1,U2,F) :- \+ larger(U1,U2), \+ larger(U2,U1), % incomparable
                             conversionFactor(U1,inch,Fa),
                             conversionFactor(inch,U2,Fb),
                             F is Fa*Fb.
conversionFactor(U,U,1.0).

There is no backtracking, because there really is no choice to be had. It's just a recursive call.

Upvotes: 0

false
false

Reputation: 10102

Here is the responsible part of your program why any of your queries does not terminate. It is not only the last one! To see this, ask Query, false in stead.

recursiveScale(Big, Scale, Smallest) :-
   (  false, findScale(Big, Scale, Smallest)
   ;  recursiveScale(Smaller, MoreScale, Smallest), false,
      findScale(Big, ScaleAgain, Smaller),
      Scale is float(ScaleAgain * MoreScale)
   ).

factor_in_inches(Unit, Scale) :-
   recursiveScale(Unit, Scale, inch), false.

convert(Unit1, Quantity1, Unit2, Quantity2) :-
    factor_in_inches(Unit1, Factor1), false,
    factor_in_inches(Unit2, Factor2),
    Factor1 is round((Factor2 * Quantity2)/ Quantity1).

?- convert(chain, 2, foot, 132.0).

This fragment called a is intimately related to your program. For, if this fragment loops, then your program will loop too. I obtained this fragment simply by adding goals false into your program. What is interesting is that all the arguments of convert/4 are effectively ignored. Therefore, all queries will loop. Even those, where you believed that they terminate.

To fix this, you have to modify something in the visible part. It seems that you misunderstood disjunction. Always put ; in front, or simply look what listing/1 suggests. For more, see tag .

Upvotes: 3

Related Questions