Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Easily replicate an element in Prolog :)

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!

like image 511
user3290526 Avatar asked Dec 07 '22 02:12

user3290526


1 Answers

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.

like image 142
lurker Avatar answered Jan 12 '23 03:01

lurker