3
votes

I m trying to check if, in a list of lists, all sublist is equal to the length of the list of lists.

For example, if I have [[1,2],[3,4]] is true, because I have 2 lists of 2 elements.

Otherwise, if I have

[[1],[2,3]] is false because have 2 lists but not all list has 2 elements

[[1,2],[2,3],[3,4]] is false because I have 2 lists and all list have 2 elements instead of two

.

I did these two function:

count([],0).
count([_H|T],N):-count(T,N1),N is N1+1 .

ma([],0).
ma([H|T],N):- count(H,M1),ma(T,N1), M1 is N1.

I did "count" (and work) for count element in a list and return N the number of elements in a list.

The "ma" function doesn't work because "count" is executed until 0, and return 2, after executing ma but until 1 step, and after making directly the M1 is N1, and obviously return false. I wish to make M1 is N1 at end of the program (like in another programming language, but I think is the then't correct form.

EDIT:

Daniel suggest to use :

ma([H],   N) :- length(H, N).
ma([H|T], N) :- length(H, N), ma(T, N).

But a list with 3 sublists all with 2 elements gives result 2, instead the result will be false(error) because N number of the list must be equal to N number of elements in ALL Sublist.

I will do on my own, without build-in predicate of prolog.

2
My name is Daniel, not David.Daniel Lyons

2 Answers

1
votes

Here a very basic solution without built in predicates:

count_elements([],N,N).
count_elements([_|T],N,N0):-
    N1 is N+1,
    count_elements(T,N1,N0).

count_length_sub([],_).
count_length_sub([H|T],N):-
    count_elements(H,0,N),
    count_length_sub(T,N).

solve(L):-
    count_elements(L,0,NO),
    count_length_sub(L,NO).

?- solve([[1,2],[3,4]]).
true.

?- solve([[1,2],[2,3],[3,4]]).
false.
1
votes

Your count/2 is like the length/2 builtin, except that the built-in has more instantiation patterns (try length(X, Y) and see). Prefer length/2.

You're right that your ma/2 predicate is unhelpful because 0 is not a length of a sublist. Basically, you've chosen the wrong base case here; your base case should be a list with exactly one item in it:

ma_1([H],   N) :- length(H, N).
ma_1([H|T], N) :- length(H, N), ma(T, N).

You will need to wrap this in something that ensures the length matches the length of the outer list:

ma(L, N) :- length(L, N), ma_1(L, N).

Note that there is no need to obtain separate variables and assert their equality (your dance with N and N1). Prolog will simply fail, which is what you want, if N does not have the right value. (Side note, do not use is for unification. The purpose of is is to reduce an arithmetic expression on the right side to a value and assign it to the variable on the left, e.g. X is 2 + 3*4.)

Another approach would be to write your actual request in a logical form and write that instead. A logical form of this request would be something like "ma(L, N) holds if N is the length of L and for all items X of L, they are lists of length N as well". This looks like so:

ma(L, N) :- 
    length(L, N), 
    forall(member(X, L), 
           length(X, N)).

This has an advantage in that no spare choice points are left around, although worrying about that is usually premature optimization.

Another approach would be to employ maplist/N, which has the advantage that it will give you back lists with variables. Unfortunately, length/2 has its parameters in the wrong order so you can't do the really cute thing and just write maplist(length(2), L). However, you can make a flip/3 predicate that flips around the arguments:

flip(P, Y, X) :- call(P, X, Y).

ma(L, N) :- length(L, N), maplist(flip(length, N), L).

Or, you can import library(yall) and use its lambda expressions:

ma(L, N) :- length(L, N), maplist({N}/[X]>>length(X, N), L).

Both of these approaches allow solutions like these:

?- ma(X, N).
X = [],
N = 0 ;

X = [[_1976]],
N = 1 ;

X = [[_1982, _1988], [_1994, _2000]],
N = 2 ;

X = [[_1988, _1994, _2000], [_2006, _2012, _2018], [_2024, _2030, _2036]],
N = 3 
...