Sicstus Prolog - Weight of a word - prolog

I've got a problem about how to weigh a word.
Every single letter in a word has specific weight, I need to calculate the total weight of the word.
For example:
A-E = 1, F-O = 2, P-Z = 3.
If the word is "PEN", the answer will be "Weight = 6",
cuz P = 3, E = 1 and N = 2.
I've tried:
word_weight([X], W):-
X = 65 -> W = 1;
X = 66 -> W = 3.
word_weight([X,Y],W):-
X = 65 -> W1 = 1;
X = 66 -> W1 = 3,
Y = 65 -> W2 = 1;
Y = 66 -> W2 = 3,
W is W1 + W2.
word_weight([X|Y], W):-
X = 65 -> W = 1;
X = 66 -> W = 3,
word_weight(Y, W).
Running res:
| ?- word_weight("B",W).
W = 3 ?
yes
It only works with one letter. How to make it works with many letters? And the answers will be the total value of the weight.

The following program works with SWI-Prolog. It will be surely easy to adapt it to Sicstus Prolog.
char_weight(C, 1) :- C >= 65, C =< 69.
char_weight(C, 2) :- C >= 70, C =< 79.
char_weight(C, 3) :- C >= 80, C =< 90.
word_weight([], 0).
word_weight([Char| Chars], Weight) :-
char_weight(Char, W),
word_weight(Chars, Ws),
Weight is W + Ws.

How about
weight(C, 1) :- char_code('A') =< C, C =< char_code('E').
weight(C, 2) :- char_code('F') =< C, C =< char_code('O').
weight(C, 3) :- char_code('P') =< C, C =< char_code('Z').
word_weight(S, W) :- string(S), !, string_list(S, L), word_weight(L, W).
word_weight([], 0).
word_weight([H|T], W) :- W is weight(H) + word_weight(T).
in ECLiPSe-CLP, string_list/2 converts a string into a list of numberic character codes, char_code/2 gets the numeric code of a character.
Edit:
Oops, I should have read your question completely:
Wen using ->/2, you should use brackets and don't hesitate to use indentation:
( Condition ->
IfBranch
;
ElseBranch
),
RestProg.
Your second clause is a bit unreadable. But for this excercise you shouldn't need ->/2 at all.
Your third clause only works for a single-letter string, because it first unifies W with the value for X and then wants to unify W with the weight of X. This only works if Y and X have the same weight.

Related

Incrementing value on backtrack

how can I do increment on backtracking ... so that goal(S) receives incremented number .. every time it fails on the next run I want to get the next number
S1 is S + 1,goal(S1)
does not work, because :
?- S=0, S1 is S+1.
S = 0,
S1 = 1.
?- S=0,between(1,3,_), S1 is S+1.
S = 0,
S1 = 1 ;
S = 0,
S1 = 1 ;
S = 0,
S1 = 1.
this work
%%counting
baz(..,C) :- .... arg(...), Y is X + 1, nb_setarg(...), goal(Y), ...
foo(..C) :- ....baz(....,C)..., foo(...C).
%%counter
blah :- ....foo(....,counter(0))...
this is not working, i think cause the recursive foo() would force baz() to initialize counter(0)... but i'm good with #sligo solution above
baz(..) :- C = counter(0), .... arg(...), Y is X + 1, nb_setarg(...), goal(Y), ...
foo(..) :- ....baz(....)..., foo(...).
so that goal(S) receives incremented number .. every time it fails on the next run I want to get the next number
That's what between/3 does? Every time on backtracking it makes the next number:
goal(X) :-
write('inside goal, X is '),
write(X),
nl.
test :-
between(0, 3, S),
goal(S).
e.g.
?- test.
inside goal, X is 0
true ;
inside goal, X is 1
true ;
inside goal, X is 2
true ;
inside goal, X is 3
true ;
Edit: From the help for between/3:
between(+Low, +High, ?Value)
Low and High are integers, High >=Low. If Value is an integer,
Low =<Value =<High. When Value is a variable it is successively
bound to all integers between Low and High. If High is inf or
infinite between/3 is true iff Value >=Low, a feature that is
particularly interesting for generating integers from a certain value.
(And see the comments on the help page by LogicalCaptain)
Use non-backtrackable destructive assignment predicate nb_setarg/3:
?- C = counter(0), between(1, 3, _), arg(1, C, X), Y is X + 1, nb_setarg(1, C, Y).
C = counter(1),
X = 0,
Y = 1 ;
C = counter(2),
X = 1,
Y = 2 ;
C = counter(3),
X = 2,
Y = 3.
Alternatives:
foo(C) :-
between(1, inf, C),
goal(C),
!.
baz(C) :-
C = counter(0),
repeat,
arg(1, C, X),
Y is X + 1,
nb_setarg(1, C, Y),
goal(Y),
!.
goal(X) :-
X > 9.
Examples:
?- foo(C).
C = 10.
?- baz(C).
C = counter(10).

How can labeling/2 generate solutions starting from the midpoint of a domain?

Having a list with independent variables, whose domain is 1..N, how can we use labeling/2 so it starts producing solutions starting from the middle?
The flags i tried are [bisect], [enum], [max], [min], [ff], but no matter which i picked, i can't make it work.
My code is:
:-use_module(library(clpfd)).
combos(EMPLOYEES,POSTS,LIST):-
LIMIT is POSTS-EMPLOYEES+1,
length(LIST,EMPLOYEES),
LIST ins 1..LIMIT,
sum(LIST,#=,POSTS),
labeling([bisect],LIST).
after setting a query, for example:
?-combos(2,10,LIST).
i want it to return:
L = [5,5];
L = [4,6];
L = [6,4] ...
instead of:
L = [1,9];
L = [2,8];
L = [3,7] ...
As a rule of thumb, whenever you try to extend the functionality of clpfd, try to reuse as much as possible. It seems that you want solutions first whose sum of distances to the center is as small as possible.
combos2(EMPLOYEES,POSTS,LIST):-
LIMIT is POSTS-EMPLOYEES+1,
length(LIST,EMPLOYEES),
LIST ins 1..LIMIT,
sum(LIST,#=,POSTS),
Mid is (LIMIT+1) div 2, %%
maplist(dist(Mid), LIST, DISTS), %%
sum(DISTS,#=,Totaldist), %%
labeling([],[Totaldist|LIST]).
dist(Mid, E, D) :-
D #= abs(Mid-E).
?- combos2(2,10,L).
L = [5,5]
; L = [4,6]
; L = [6,4]
; L = [3,7]
; L = [7,3]
; ... .
Here you go!
combos(2,S,L) :- b2(S,L).
combos(C,S,[A|L]) :-
C > 2,
b2(S,[A,B]),
D is C-1,
combos(D,B,L).
b2(S,L) :- B is S-1, bisector(B,L).
bisector(Y,[A,B]) :-
odd(Y),
M is div(1+Y,2),
Z is M-1,
range(D,0,Z),
bisec1(D,M,A,B).
bisector(Y,[A,B]) :-
even(Y),
M is 1+Y,
Z is Y/2-1,
range(D,0,Z),
bisec2(D,M,A,B).
bisec1(0,M,M,M).
bisec1(D,M,A,B) :- D > 0, A is M + D, A > 0, B is M - D, B > 0.
bisec1(D,M,A,B) :- D > 0, A is M - D, A > 0, B is M + D, B > 0.
bisec2(D,M,A,B) :- A is (M+2*D+1)/2, A > 0, B is (M-2*D-1)/2, B > 0.
bisec2(D,M,A,B) :- A is (M-2*D-1)/2, A > 0, B is (M+2*D+1)/2, B > 0.
even(X) :- 0 is mod(X, 2).
odd(X) :- 1 is mod(X, 2).
range(M,M,_).
range(X,M,N) :- P is M + 1, P =< N, range(X,P,N).

Fibonacci in Prolog - Breaks if False

I have a program fib(X,Y). If Y is the Xth Fibonacci number it returns True else it should return False. My program breaks anytime I input statement which is false.
fib(R,V) :- fib(0,1,R,V).
fib(X, Y, 0, V) :- Y == V.
fib(X, Y, R, V) :- Z is X + Y, C is R - 1, fib(Y, Z, C, V).
fib(0,1) -> True
fib(1,1) -> True
fib(2,2) -> True
fib(3,3) -> True
fib(4,5) -> True
fib(3,5) -> Won't finish.
What do I do wrong? I am using https://swish.swi-prolog.org/ to run my program queries.
The problem here is that you write two clauses fib(X, Y, 0, V) :- and fib(X, Y, R, V) :-. Prolog uses backtracking: in case one clause has been tried, it wil - regardless of sucess or failure - also later retry the next clause (there are some meta-predicates like once/1 that can alter this).
So even if R is 0, or lower, Prolog will also try the second clause.
A quick way to fix this is by using a guards for the second clause:
fib(_, Y, 0, V) :-
Y == V.
fib(X, Y, R, V) :-
R > 0,
Z is X + Y,
C is R - 1,
fib(Y, Z, C, V).
Furthermore your code is not very elegant in the sense that you can not use the relation in a reversed way, nor can we query for the X-th element.
For instance you use Y == V, but this blocks unification: if we want to know the X-th fibonacci number, we want a way to propagate the result back. So we can use unification instead:
fib(_, V, 0, V).
fib(X, Y, R, V) :-
R > 0,
Z is X + Y,
C is R - 1,
fib(Y, Z, C, V).
But now we still do not have a bidirectional relation: we can not obtain the X for a given value V. This is more complex. The easiest way is probably using clpfd for this:
:- use_module(library(clpfd)).
fib(_, V, 0, V).
fib(X, Y, R, V) :-
R #> 0,
V #>= Y,
Z is X + Y,
C #= R - 1,
fib(Y, Z, C, V).
Now we can:
enumerate all indices and the corresponding Fibonacci numbers:
?- fib(A,B).
A = 0,
B = 1 ;
A = B, B = 2 ;
A = B, B = 3 ;
A = 4,
B = 5 ;
A = 5,
B = 8 ;
A = 6,
B = 13 ;
A = 7,
B = 21
...
Obtain the i-th Fibonacci number:
?- fib(2,B).
B = 2 ;
false.
?- fib(10,B).
B = 89 ;
false.
obtain the i for which the corresponding Fibonacci number is a certain value:
?- fib(A,1).
A = 0 ;
A = 1 ;
false.
?- fib(A,2).
A = 2 ;
false.
?- fib(A,3).
A = 3 ;
false.
?- fib(A,4).
false.
?- fib(A,5).
A = 4 ;
false.
Check if the i-th Fibonacci number is a given value:
?- fib(4,5).
true ;
false.
?- fib(4,6).
false.
?- fib(4,10).
false.
?- fib(5,8).
true ;
false.

Prolog - summing up predicate results

Let's say i have the following predicate:
func1(X,B,R)
I provide it with a certain X and B and in return it gives me 5 different results for R.
EDIT:
The X and B do not specify a range. rather, X specify an integer (say 120) and B specifies all integers (starting from 1) whose cubic is less than X.
What func1 does is calculating R as the result the remainder.
In this case where X=120:
B = 1, R = 119 (120-1^3)
B = 2, R = 112 (120-2^3)
B = 3, R = 93 (120-3^3)
B = 4, R = 56 (120-4^3)
It would not calculate B=5 since 5^3 = 125 which is greater than 120, so it stops here.
How can i make a predicate such as:
func2(R,S)
That would accept all of the results given by R, sum them up and store them in S?
Thanks!
To start with, since the values of B are totally derivable from the value of X, I wouldn't include both as arguments in func1. I'll introduce a predicate func3/2 which is true if the second argument is derivable from the first (i.e., func3(X, B) is true if B is derivable from X):
func1(X, R) :-
func3(X, B),
...
What will happen if you query func1(120, R) is you'd get one or more results for R. Then you can use findall/3 as I indicated in my comment:
func2(X, S) :-
findall(R, func1(X, R), Rs),
sumlist(Rs, S).
To define func3/2 the cleanest approach would be to use CLP(FD):
:- use_module(library(clpfd)).
func3(X, B) :-
B #>= 0,
(X - B^3) #>= 0,
label([B]).
Here's an example of what func3 does:
?- func3(120, B).
B = 1 ;
B = 2 ;
B = 3 ;
B = 4.
A much less desirable way to do this if you can't use CLP(FD) would be to use between and define the upper limit of B to be the greatest integer not exceeding the cube root of X:
func3(X, B) :-
Limit is floor(exp(log(X) / 3)),
between(1, Limit, B).
Which yields the same result as above.

Prolog combinations with constraints

I am looking for a way to find all possible solutions given a certain set of constraints. I had prolog in school but it has been a while so consider me fairly new. What I want to achieve is something like this:
fC(U,V,X,Y,Z):-
(U*2 + V - Y -(2*Z)) =< -5,
(U*2 + V - Y -(2*Z)) >= -108,
(U+V+X+Y+Z) =:= 54.
U, V, X, Y, and Z are non negative numbers. They only have 2 rules to compute them: be between -5 and -108 (when multiplied with certain weight which I tried to formulate in the code above) and added together be exactly 54.
I tried generating 5 lists of 0 to 54, find all combinations and then go over them to check my 'constraints', I quickly ran out of memory so I must be doing something wrong.
Kind regards,
Jelle
For integers, it is easy with clpfd constraints. For example, in SICStus Prolog or SWI:
:- use_module(library(clpfd)).
fC(U,V,X,Y,Z):-
U*2 + V - Y -2*Z #=< -5,
U*2 + V - Y -2*Z #>= -108,
U+V+X+Y+Z #= 54.
You already obtain residual goals with the most general query:
?- fC(U, V, X, Y, Z).
X+U+V+Z+Y#=54,
2*Z+Y#=<2*U+V+108,
2*U+V+5#=<2*Z+Y.
This of course does not help much by itself in this case.
To get concrete solutions, use labeling/2. For example:
?- fC(U, V, X, Y, Z), Vs = [U,V,X,Y,Z], Vs ins 0..sup, label(Vs).
The constraint Vs ins 0..sup states that all variables are non-negative. In SICStus Prolog, use domain(Vs, 0, sup).
Sample solutions:
U = V, V = X, X = Y, Y = 0,
Vs = [0, 0, 0, 0, 54],
Z = 54 ;
U = V, V = X, X = 0,
Vs = [0, 0, 0, 1, 53],
Y = 1,
Z = 53 ;
etc.
For constraints over rational numbers, check out clpq constraints and library(clpq).
a 'low-tech' alternative... but really, use library(clpfd)
?- maplist(between(0,54),[U,V,X,Y,Z]),fC(U,V,X,Y,Z).
U = V, V = X, X = Y, Y = 0,
Z = 54 ;
U = V, V = X, X = 0,
Y = 1,
Z = 53 ;
U = V, V = X, X = 0,
Y = 2,
Z = 52 ;
...

Resources