3

I've just been starting out in Prolog and I was hoping to perform the following task:

Make a predicate A(P,N,L) such that for all C which is nth element of L, P(N,C).

Basically I want to perform a map on the range [0..N]. In Haskell, the language I am most familiar with, this would look like

f p n = map(p)[0..n]

(Haskell doesn't quite have predicates so I'm taking some liberties here)

or in pointfree

f = (.enumFromTo 0).map

And it seems like I should be able to do it in Prolog easily enough. Prolog's maplist/3 is basically already that so it should be a trivial modification. My definition should look something like:

A(P,N,L) :- maplist(P, ??? , L).

However I can't really figure out what to put in the blank. In Haskell I would use a function like enumFromTo, but it seems that such a thing doesn't exist in Prolog. The closes equivalent would be between/3, but that isn't a list so I can't use for maplist.

Alternatively I could make my own range predicate.

The first thing I tried was:

range(0,[0]).
range(N,[N|T]) :- range(N-1,T).
A(P,N,L) :- range(N,rangeN), maplist(P, rangeN, L).

But I can't get that to resolve at all. I also tried

range(N,L):-findall(X,between(0,N,X),L),sort(L,L).
A(P,N,L) :- range(N,rangeN), maplist(P, rangeN, L).

But that just seems really clunky for such a small problem.

How might I fill in the gap in my maplist? Am I approaching the problem in the wrong way?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Wheat Wizard
  • 3,982
  • 14
  • 34
  • 1
    Wrong approach I think. Haskell is lazy, so mapping over a list is fine as the whole list never exists at one time. In Prolog you should do a recursive predicate that just updates an integer. – Tomas By Dec 18 '17 at 11:26

2 Answers2

2
-- % f p n = map (p) [0..n] = [p 0, p 1, p 2, ..., p n]

is translated into Prolog as

f(P,N,L):- f(P,0,N,L).
f(P,I,N,[]):- I > N.
f(P,I,N,L):- call(P,I,X), 
             ( N =:= I -> L = [X]
             ; L = [X|T], J is I+1, f(P,J,N,T) ).

This assumes p :: Int -> a for some a, as implied by the Haskell code.

This also assumes you are given a concrete ("ground") callable two-argument predicate P and an integer number N.

Another possibility is

g(P,N,L):- findall(X, (between(0, N, I), call(P,I,X)), L).

This finds all Xs such that (0 <= I <= N and P(I,X)) holds.

Tested in SWI-Prolog:

11 ?- [user].
add1(X,Y):- Y is X+1.
|: 

12 ?- f(add1,5,L).
L = [1, 2, 3, 4, 5, 6].

13 ?- g(add1,5,L).
L = [1, 2, 3, 4, 5, 6].
Will Ness
  • 70,110
  • 9
  • 98
  • 181
2

When I was trying to code in Prolog a solution to N queens problem, as stated from Picat home (see Example 5, near end of page), I faced a similar problem. Here is my final outcome, with commented some alternatives:

:- use_module(library(clpfd)).

queens(N, Q) :-
    length(Q, N),
    Q ins 1..N,
    all_different(Q),
    maplist_index([I,V,P]>>(P#=V+I),1,Q,Ps),all_different(Ps),
    maplist_index([I,V,M]>>(M#=V-I),1,Q,Ms),all_different(Ms),
    /* no
    bagof(P, (nth1(I,Q,V), P #= V + I), Ps), all_different(Ps),
    bagof(M, (nth1(I,Q,V), M #= V - I), Ms), all_different(Ms),
    */
    /* ok
    all_different_p(Q, 1, P), all_different(P),
    all_different_m(Q, 1, M), all_different(M),
    */
    label(Q).

all_different_p([Q|Qs], I, [P|Ps]) :-
    P #= Q + I,
    succ(I, J),
    all_different_p(Qs, J, Ps).
all_different_p([], _I, []).

all_different_m([Q|Qs], I, [P|Ps]) :-
    P #= Q - I,
    succ(I, J),
    all_different_m(Qs, J, Ps).
all_different_m([], _I, []).

maplist_index(P, I, [X|Xs], [Y|Ys]) :-
    call(P, I, X, Y),
    succ(I, J),
    maplist_index(P, J, Xs, Ys).
maplist_index(_, _, [], []).

maplist_index/4 is an example of what you need. It's worth to note that bagof/3 doesn't work well in presence of attributed variables.

CapelliC
  • 59,646
  • 5
  • 47
  • 90