Easily replicate an element in Prolog :) - list

I am working on a longer problem that has me duplicate an element N times in list form, and I believe that using append is the right way to go for this. The tiny predicate should theoretically act like this:
?- repl(x,5,L).
L = [x, x, x, x, x] ;
false.
I cannot seem to find any tips for this online, the replication of a single element, but I believe we need to use append, but no recursive solution. I come from more of a Haskell background, where this problem would be much easier to perform. Can someone help get me started on this? :)
Mine so far:
repl(E, N, R) :-
N > 0, append([E], [], R), writeln(R), repl(E, N-1, R), fail.
Which gives me:
?- repl(x,5,L).
[x]
[x]
[x]
[x]
[x]
false.
Close but not quite!

A recursive approach would be straight-forward and would work. I recommend figuring that one out. But here's a fun alternative:
repl(X, N, L) :-
length(L, N),
maplist(=(X), L).
If N is instantiated, then length(L, N) will generate a list of length N of just "blanks" (don't care terms). Then maplist(=(X), L) will unify each element of L with the variable X.
This gives a nice, relational approach and yields sensible results in the general case:
| ?- repl(X, N, L).
L = []
N = 0 ? ;
L = [X]
N = 1 ? ;
L = [X,X]
N = 2 ? ;
| ?- repl(X, N, [x,x,x]).
N = 3
X = x
yes
...
To figure out a recursive case, think about what your base case looks like (it would be repl with a count of 0 - what does the list look like then?). In the recursive case, think in terms of:
repl(X, N, [X|T]) :- ...
Meaning: The list [X|T] is the element X repeated N times if.... Figure out if what? If your base case is length 0, then your recursion is probably going to describe the repl of a list of length N in terms of the repl of a list of length N-1. Don't forget in this recursive rule to ensure N > 0 to avoid infinite recursion on backtracking. If you don't need the predicate to be purely relational and assume N is instantiated, then it can be fairly simple.
If you make a simple recursive version, you can "wrap" it in this predicate to make it work with variable N:
repl(X, N, L) :-
length(L, N),
simple_recursive_repl(X, N, L).
...
Because length/2 is relational, it is much more useful than just providing the length o a given list. When N and L are not instantiated, it becomes a generator of variable lists, starting at length 0. Type, length(L, N). at the Prolog prompt and see what happens.

Determinism
You give the following example of the predicate you envision:
?- repl(x,5,L).
L = [x, x, x, x, x] ;
false.
Notice that the ; is not very productive here. If you want to repeat x 5 times, then this can be done in exactly one way. I would therefore specify this predicate as deterministic not nondeterministic as you are doing.
Repeating list
Your code is actually quite far off a working solution, despite the output looking quite close in spirit to the envisioned result. You try to define the base case and the recursive case at the same time, which will not work.
Here is a simple (but less fun than #lurker gave :-)) implementation of the base and recursive case:
repeating_list(_, 0, []):- !.
repeating_list(H, Reps1, [H|T]):-
Reps2 is Reps1 - 1,
repeating_list(H, Reps2, T).
In a sense #lurker's implementation is simpler, and it is surely shorter.
Some extensions
In real-world/production code you would like to catch type errors and treat different instantiations with the same predicate. The second clause checks whether a given list consists of repeating elements (and if so, which one and how many occurrences there are).
%! repeating_list(+Term:term, +Repeats:integer, -List:list(term)) is det.
%! repeating_list(?Term:term, ?Repeats:integer, +List:list(term)) is det.
repeating_list(_, 0, []):- !.
% The term and number of repetitions are known given the list.
repeating_list(H, Reps, L):-
nonvar(L), !,
L = [H|T],
forall(
member(X, T),
% ==/2, since `[a,X]` does not contain 2 repetitions of `a`.
X == H
),
length([H|T], Reps).
% Repetitions is given, then we generate the list.
repeating_list(H, Reps1, [H|T]):-
must_be(nonneg, Reps1), !,
Reps2 is Reps1 - 1,
repeating_list(H, Reps2, T).
% Repetitions is not `nonneg`.
repeating_list(_, Reps, _):-
domain_error(nonneg, Reps).
Notice that I throw a domain error in case the number of repetitions is negative. This uses library error in SWI-Prolog. If your Prolog does not support this feature, then you may leave the last clause out.
PS: Comparison to Haskell
The combination of your statement that you do not know how to solve this problem in Prolog and your statement that this problem can be solved much easier in Haskell seems a little strange to me. I think you can only compare the difficulty of two implementations once you know how both of them look like.

I do prefer findall/3 to build lists, and between/3 to work with ranges:
repl(E, N, L) :- findall(E, between(1, N, _), L).

Related

creating list of an elemente multipled n times

So my problem is this, i have a predicate which is repete_el(El,N,L) in which El is an element, N is the number of times is repeated and L is the list contaning that element repeated N times.
My problem is that istead of repeating the element its giving false and i dont understand why.
Example:
My output:
?- repete_el(a,3,L).
false
Correct output:
?- repete_el(a,3,L).
L = [a,a,a].
Program:
repete_el(El,0,[]) :- !.
repete_el(El,N,L) :- repete_el(El,N,L,[],N).
repete_el(El,N,L,L2) :- length(L2,C),
C =< N,
append(L2,[N],NL),
repete_el(El,N,L,NL).
By the way, i can only do this iteratively.
You can use the standard findall/3 predicate and the de facto standard between/3 predicate. For example:
| ?- findall(a, between(1,5,_), List).
List = [a, a, a, a, a]
yes
The reason this will not work is because at the moment you call repete_el/4, L2 is a free variable, so length(L2, C) will start building all sorts of lists with lengths. Then you make a recursive call on a list NL with one element extra, and you require that list to have again length C (which should again be smaller than N). But eventually C will be larger than N, and thus the predicate will fail.
You can write such predicate as:
repete_el(_, 0, []). %% (1)
repete_el(X, N, [X|T]) :- %% (2)
N > 0,
N1 is N-1,
repete_el(X, N1, T).
Here we thus say:
(1) A list where we repeat an element 0 times is an empty list; and
(2) a list where we repeat X, N times, with N greater than 0 is a list that starts with X and ends with a list where we repeat X, N-1 times.

Writing Prolog Code which returns list of integer sums from a given number

I'm trying to write a Prolog predicate that can decomposes a given non-negative integer into every possible sum, using a DCG.
For example:
?- s(3, L, []).
L = [3] ? ;
L = [2,1] ? ;
L = [1,2] ? ;
L = [1,1,1] ? ;
false.
I started by writing a predicate which takes a number N and returns L = [1,2,3,...,N]:
mkList(N, L) :-
m(0, N, L).
m(X, X, []).
m(Y, L, [H|T]) :-
H is Y+1,
m(H, L, T).
However, I'm not sure how I can proceed.
s(Input) -->
{ mkList(Input, InputList) },
{ member(X, InputList) },
[X].
This is what I was going to use, it starts out my running through the list one by one. However, I'm not sure where I should include a rule to find the difference between X and Input.
the base case is easy:
all_sum(N) --> [N].
now, we can call recursively if we provide a M between 1 and N, and take the rest R (beware it must be > 0)
all_sum(N) --> {...},[M],all_sum(R).
please fill the dots using the hints above.
You will get
?- phrase(all_sum(3),L).
L = [3] ;
L = [1, 2] ;
L = [1, 1, 1] ;
L = [2, 1] ;
false.
The best way to proceed is to think like Prolog, that is, recursively. Yes, you've got recursion. It may even be right, but I'm not following it.
Thinking like this should work:
mkList(Number,List) :-
pick a number between 1 and number. It'll be your first addend.
subtract it from number to get the remainder.
make a recursive call to handle the remainder.
patch together List based on the first addend and the list from the recursive call.
Obviously we need to stop when Number is less than 1.
This doesn't use a DCG, but for the life of me I can't see how a DCG is relevant here.

Finding Length of List in Prolog

I'm running Prolog and trying to write a small function returning the length of a list:
len([],0).
len([XS], Y) :-
len([X|XS], M),
Y is M+1.
My logic is that the recursive call should include the tail of the list (XS) and increase 1 to the previous length (Y is M+1.)
This always returns false.
Any thoughts?
Here is a general methodology for debugging and testing Prolog predicates:
Start with the most general query!
Think of it: In Prolog you do not need to make up some test data. You don't even need to understand a predicate at all: Just hand in free variables! That is always a professional move!
So in your case, that's
?- len(L,N).
L = [], N = 0
; loops.
Your definition is not that bad as you claim: At least, it is true for the empty list.
Now, maybe look at the compiler warnings you probably received:
Warning: user://1:11:
Singleton variables: [X]
Next read the recursive rule in the direction of the arrow :- that is, right-to-left:
Provided len([X|Xs], M) is true and Y is M+1 is true, provided all that is true, we can conclude that
len([XS], Y) is true as well. So you are always concluding something about a list of length 1 ([Xs]).
You need to reformulate this to len([X|Xs], M) :- len(Xs, N), Y is M+1.
And here is another strategy:
Generalize your program
By removing goals, we can generalize a program1. Here is my favorite way to do it. By adding a predicate (*)/1 like so:
:- op(950,fy, *).
*_.
Now, let's remove all goals from your program:
len([],0).
len([XS], Y) :-
* len([X|XS], M),
* Y is M+1.
What we have now is a generalization. Once again, we will look at the answers of the most general query:
?- len(L, N).
L = [], N = 0
; L = [_].
What? len/2 is only true for lists of length 0 and 1. That means, even len([1,2], N) fails! So now we know for sure: something in the visible remaining part of the program has to be fixed. In fact, [XS] just describes lists of length 1. So this has to be removed...
Fine print:
1 Certain restrictions apply. Essentially, your program has to be a pure, monotonic program.

Finding the most similar list from predicate knowledge base in Prolog

I have a problem in which I have a list of elements and I have to cycle through all instances of a particular /2 predicate to find which one has he highest number of matching elements in its list. In terms of implementation I can't seem to figure out how I should be updating the highest match so far and then stopping when there are no more.
findAnswer(MyList, HighMatchNum,_):-
answer(X,Y),
myIntersection(MyList, Y, NUM), //handles a single instance check and returns how many elements match.
NUM > HighMatchNum,
findAnswer(MyList, NUM, answer(X,Y)).
//Knowledge base
answer(sample1, [a,b,c,d]).
answer(sample2, [d,c,e]).
there is library(aggregate):
findAnswer(MyList, HighMatchNum, K) :-
aggregate_all(max(N, Key),
( answer(Key, List),
myIntersection(MyList, List, N)
),
max(HighMatchNum, K)).
myIntersection(MyList, List, N) :-
intersection(MyList, List, L),
length(L, N).
% Knowledge base
answer(sample1, [a,b,c,d]).
answer(sample2, [d,c,e]).
yields
?- findAnswer([a], C, K).
C = 1,
K = sample1.
?- findAnswer([d,e], C, K).
C = 2,
K = sample2.
To find the best, we have to search through the whole list, to its end. We will maintain the best so far and its score as additional arguments:
best_match(MyList,R-RN):-
findall(X, (answer(A,L), X=A-L), ALL),
ALL = [A-L|T],
myIntersection(MyList, L, N),
find_best(MyList,T,A,N,R,RN).
find_best(_,[],A,N,A,N).
find_best(MyList,[B-H|T],A,N,R,RN):-
myIntersection(MyList, H, K),
( K>N -> find_best( MyList, T, B, K, R, RN)
; find_best( MyList, T, A, N, R, RN ).
this produces the name and score of the best match.
Simply assert it, I can not see how you can propagate the max value in your solution.
:- dynamic maxval/1
:- maxval(0).
findAnswer(MyList, HighMatchNum) :-
answer(X,Y),
myIntersection(MyList, Y, NUM), %handles a single instance check and returns how many elements match.
NUM > HighMatchNum, %If this fails, try other answer
retract(maxval(_), assert(maxval(X)),!, %else retract the previous value and assert the new one
findAnswer(MyList, NUM).
Finally check the value of maxval/1 as maxval(X). This algorithm will always fail so you will get the solution in the user database, the problem is with your implementation, you may check your logic. However it will assert the proper answer. You must remember to always implement a base case for any recursive procedure.

Prolog Create a List

I have to create list of n elements for example,
do_list(5,L1).
should return,
L1=[1,2,3,4,5].
This is what I have, but it's not working.
do_list(X,L1):- X1 is X-1, do_list(X1,[X1|L1]).
do_list(0,[]).
If you want to create a list of consecutive numbers from 1 to N you can use builtin predicates findall/3 and between/3 this way:
do_list(N, L):-
findall(Num, between(1, N, Num), L).
?- do_list(5,L).
L = [1, 2, 3, 4, 5].
SWI also has another builtin which does just that, numlist/3:
?- numlist(1,5,L).
L = [1, 2, 3, 4, 5].
There are three problems with your code. The first problem is that you add X1 to the list in the clause body, but you never pass the new list back towards the head of the clause. I.e., L1 is an accumulator variable, but you need a third argument that will be bound to the final list.
The second is that the second clause only matches if the input list is empty. This will never be the case, since you add X1 to the list before calling do_list/2 recursively. I.e., you don't have a recursion anchor, and the goal ?- do_list(5,L) will never return.
The third problem is that you add X1 to the list instead of X. You'd skip the largest number.
This is how it should work:
do_list(N, L) :- do_list1(N, [], L).
do_list1(0, L, L) :- !.
do_list1(N, R, L) :- N > 0, N1 is N-1, do_list1(N1, [N|R], L).
Or if you don't want to use any built-in function (like me, when I tried this as a practice and then run into this question), you can use this (working but not effective) solution:
connect([],X,X).
connect([H|T],C,[H|T2]) :- connect(T,C,T2).
revert([],[]).
revert([H|T],R) :- revert(T,Trev), connect(Trev,[H],R)
do_revlist(0,[]).
do_revlist(X,[X|L]) :- X1 is X-1, do_revlist(X1,L).
do_list(X,L2) :- do_revlist(X,L), revert(L,L2).
P.S. Works only for positive integers.
Another solution similar to that of twinterer but without cut or predefined predicates, employing an accumulator.
do_List(Max,L) :- do_ListAcc(1,Max,L). % call Accumulator
do_ListAcc(N,N,[N]). % N=:=N ends recursion
do_ListAcc(Min,Max,[Min|Succs]) :-
Next is Min + 1,
do_ListAcc(Next,Max,Succs).
This also works only for positive integers.