2
votes

I am trying to write a short Prolog program which takes a list of numbers and returns a list where all numbers have been squared.

Ie: [2,4,5] => [4,16,25]

My code so far:

list_of_squares([X], L) :-
    L is X^2.
list_of_squares([X|XS], [X^2|M]) :-
    list_of_squares(XS, M).

For some reason though Prolog doesn't like me squaring X while adding it to a list... Any thoughts on how I could do this?

3
You could just do list_of_squares(L, S) :- maplist(square, L, S). square(X, XS) :- XS is X^2. - lurker

3 Answers

3
votes

You're not that far off, but you make two small mistakes:

Firstly, you mix element X with list L. Your first clause should be:

list_of_squares([X], [Y]):-
  Y is X ^ 2.

Secondly, you cannot perform an arithmetic function in list notation. Your second clauses should be as follows:

list_of_squares([X|Xs], [Y|Ys]):-
  Y is X ^ 2,
  list_of_squares(Xs, Ys).

Thirdly, there is a more fundamental problem. With the first two fixes, your code works, but the base case, i.e. the first clause, is not that well chosen. (A) the code cannot process the empty list. (B) For a singleton list the code is needlessly nondeterministic, because both clauses apply. This is solved by choosing the base case wisely:

squares([], []).
squares([X|Xs], [Y|Ys]):-
  Y is X ^ 2,
  squares(Xs, Ys).
1
votes

Here is a general method how you can localize such an error. First, let's start with your exemple:

?- list_of_squares([2,4,5],[4,16,25]).
false.

Oh no! It fails! There is a very general method what to do in such a situation:

Generalize the query

So we replace [4,16,25] by a new, fresh (ah, true freshness!) variable:

?- list_of_squares([2,4,5],L).
L = [2^2, 4^2|25] ;
false.

That's way better: Now you know that there is a "result", but that result it not what you expected.

Next,

Minimize the query

The list is way too long, so I will chop off some elements. Say, the first two:

?- list_of_squares([5],L).
L = 25 ;
false.

Again, wrong, but smaller. Now, where is the error for that? To get it

Specialize your program

list_of_squares([X], L) :-
    L is X^2.
list_of_squares([X|XS], [X^2|M]) :- false,
    list_of_squares(XS, M).

That program, again gives the same wrong answer! So in there is a bug in the visible part. What we expect is

?- list_of_squares([5],[25]).
false.

this to succeed. But where is the error? Again:

Generalize the query

?- list_of_squares([5],[X]).
false.

HET!

Now, you should realize that that rule might be:

list_of_squares([X], [Y]):-
  Y is X ^ 2.

And the same (is)/2 should be used in the recursive rule. And, why not accept [].

I, personally, would rather write using library(lambda):

list_of_squares(Xs, Ys) :-
   maplist(\X^XX^(XX is X^2), Xs, Ys).

Or, even better, using library(clpfd)

list_of_squares(Xs, Ys) :-
   maplist(\X^XX^(XX #= X^2), Xs, Ys).
0
votes

Prolog doesn't have a 'functional' mindset, but some standard builtin predicate can help working with lists. In this case

list_of_squares(L,S) :- findall(Sq,(member(E,L),Sq is E*E),S).

?- list_of_squares([2,4,5], R).
R = [4, 16, 25].

in this case, member/2 play a role similar to lambda expressions, giving a 'name' to each element E available in L. findall/3 compute all solutions of its goal ,(member(E,L),Sq is E*E),, and collects results (the 'template' expression, that is, Sq).