1

I am trying to find a value score, which can be calculated imperatively from two equal length lists Xs, Ys as:

for i in len(Xs):
    if Xs[i] == Ys[i]:
        score++
    else:
        score--

So the idea is to basically check every element from left to right on both lists and increment score if those elements are the same, decrement score otherwise. For example:

score([1,2,3], [1,2,3]) == 3
score([1,2,3], [1,2,1]) == 1
score([1,2,3], [3,2,1]) == -1
score([1,2,3], [3,1,2]) == -3

I think I actually managed to write the program as:

score(Xs, Ys, C) ?=>
    Xs = [], Ys = [], C = 0.

score(Xs, Ys, C) =>
    Xs = [XH|XT],
    Ys = [YH|YT],
    if (XH #= YH) then
        C #= C1 + 1
    else
        C #= C1 - 1
    end,
    score(XT, YT, C1).

the code gives the correct results when I query the score:

score([1,2,3], [1,2,3], S).
    S = 3 ?;

score([1,2,3], [1,2,1], S).
    S = 1 ?;

score([1,2,3], [3,2,1], S).                                      
    S = -1 ?;

score([1,2,3], [3,1,2], S).                                      
    S = -3 ?;

However, when I try to generate the lists which produce the scores, the code only generates the identical list with score 3:

S :: -3..3, Ls = new_list(3), Ls :: 1..3, score([1,2,3], Ls, S), solve([Ls, S]).

S = 3
Ls = [1,2,3] ?;

no

I want to generate all the lists, corresponding to all the possible scores, I have a feeling that I have to modify the base case but I can't figure out how :)

EDIT: I also tried to do a tail-recursion type of solution, it again gives correct results when the scores are queried, but it can only generate the identical list.

score(Xs, Ys, C) ?=>
    score_helper(Xs, Ys, 0, C).

score_helper([], _, Cur, C) ?=>
    C = Cur.

score_helper(Xs, Ys, Cur, C) ?=>
    Xs = [XH|XT],
    Ys = [YH|YT],
    if (XH = YH) then
        score_helper(XT, YT, Cur+1, C)
    else
        score_helper(XT, YT, Cur-1, C)
    end.
mlg556
  • 419
  • 3
  • 13

1 Answers1

1

If you are using constraint solver (such as cp, sat, etc) then simply using sum should do the job:

import cp. 

go ?=> 
  X=new_list(3),
  Y=new_list(3),
  X::1..3, 
  Y::1..3, 
  Z #= sum([cond(X[I] #= Y[I], 1,-1) : I in 1..3]),
  solve(X++Y),
  println([x=X,y=Y,z=Z]), 
  fail,
  nl.
go => true.

The main point is that one can use constraints (here X[I] #= Y[I]) inside the list comprehension.

Or is it a requirement that it should no use constraints?

Edit: Another CP approach is the following (using "Prolog" style which is supported by Picat v3. This use equivalence (#<=>) for the check:

go3 ?=>
  Y = [1,2,3],
  X = new_list(3),
  X :: 1..3,
  Score :: [-1,1],
  score3(X,Y,0,Score),
  solve(X ++ [Score]),
  println(x=X),
  println(score=Score),
  nl,
  fail,
  nl.
go3 => true.


score3([], [], Score,Score).
score3([XH|XT], [YH|YT], Score0, Score) :-
  (XH #= YH) #<=> (Score1 #= Score0 + 1),
  score3(XT, YT, Score1,Score).

One problem with your approach is the you mix constraint equality check (#=) with traditional equality (== and =) which might not give the expected results.

And here's yet another approach using the same principle but inside a foreach loop:

 go2 ?=>
   Y = [1,2,3],
   N = Y.len,
   X = new_list(N),
   X :: 1..max(Y),
   Scores = new_list(N),
   Scores :: [-1,1],
   foreach(I in 1..X.len)
      X[I] #= Y[I] #<=> Scores[I] #= 1
   end,
   Z #= sum(Scores),
   solve(X ++ Scores),
   println([x=X,y=Y,scores=Scores,z=Z]),
  fail,
  nl.
go2 => true.
hakank
  • 6,629
  • 1
  • 17
  • 27
  • 1
    thank you for the answer! I actually wanted to use constraints so that works just fine, I will surely check the other approaches as well. – mlg556 Jan 15 '21 at 12:03