2
votes

I have the following code:

check([],[]).
check([X], [Y]) :-
    X > 0,
    Y is 1.
check([X], [Y]) :-
    X =:= 0,
    Y is 0.
check([L1|Tail], [L2|Tail2]) :-
    L1 > 0,
    L2 is 1,
    check(Tail,Tail2).
check([L1|Tail], [L2|Tail2]) :-
    L1 =:= 0,
    L2 is 0,
    check(Tail,Tail2).

the predicate check creates a table that replace all the items bigger than 0 into 1. this predicate works for a simple list like this L = [3,4,5,6,0] and produces a list L1 = [1,1,1,1,0].

I need to make the predicate check to accept lists which have lists as items.

For instance : L = [[2, 3, 4], [4, 0, 6], [5, 6, 3]]. The items of the list are as many are the items of an item list. This means that if the list contains 3 items-lists ,each item-list should contain 3 items.

2
It's not clear to me what you're trying to accomplish. Could you please post the expected output of your example list of lists? (BTW in your original code, the 2nd and 3rd predicates are redundant, and can be removed) - mgibsonbr
What about negative numbers? Your code suggests failure, your text suggests that they are left as is. - false

2 Answers

6
votes

For relations that are the same for each item in a list, it is often best to describe the relation for a single element, and then to use maplist/3:

check(0, 0).
check(N, 1) :- N > 0.

Sample query:

?- maplist(check, [3,4,5,6,0], Ls).
Ls = [1, 1, 1, 1, 0] ;
false.

Now the case of nested lists translates into a nested maplist/3:

?- maplist(maplist(check), [[2, 3, 4], [4, 0, 6], [5, 6, 3]], Ls).
Ls = [[1, 1, 1], [1, 0, 1], [1, 1, 1]] ;
false.
0
votes

First of all, your second and third clauses aren't needed, they just produce duplicate solutions, here is a better check/2 predicate :

check([],[]).
check([L1|Tail], [L2|Tail2]) :-
    L1 > 0,
    L2 is 1,
    check(Tail,Tail2).
check([L1|Tail], [L2|Tail2]) :-
    L1 =:= 0,
    L2 is 0,
    check(Tail,Tail2).

Then, instead of using is/2 to assign values to L2, you could directly specify L2 value in the head of your clauses, like that :

check([],[]).
check([L1|Tail], [1|Tail2]) :-
    L1 > 0,
    check(Tail,Tail2).
check([L1|Tail], [0|Tail2]) :-
    L1 =:= 0,
    check(Tail,Tail2).

Then, instead of using =:=/2, you could once again specify directly the value in the head of your second clause :

check([],[]).
check([L1|Tail], [1|Tail2]) :-
    L1 > 0,
    check(Tail,Tail2).
check([0|Tail], [0|Tail2]) :-
    check(Tail,Tail2).

Now your check/2 predicate already looks a lot better!

Let's add a clause to handle list nesting:

check([],[]).
check([L1|Tail], [L2|Tail2]) :-
    L1 = [_|_],
    check(L1, L2),
    check(Tail, Tail2).
check([L1|Tail], [1|Tail2]) :-
    \+ L1 = [_|_],
    L1 > 0,
    check(Tail,Tail2).
check([0|Tail], [0|Tail2]) :-
    check(Tail,Tail2).

As you saw, we also needed to add a test that L1 isn't a list in our previous clause, so that a list wouldn't go this way and produce strange solutions.

Hope it helped.