views:

100

answers:

1

I need write a set of clauses that take a list of integer lists and return a single list with all the elements doubled.

For example:

?- double([[1,2],[3]], X).
Yes
X = [2,4,6]

I have a set of clauses called mega_append that return a single list from a list of lists.

For example:

?- mega_append([[1,2],[3]], X).
Yes
X = [1,2,3]

Here is my progress (m_a is short for mega_append):

double([],[]).
double(List,[H1|T1]) :-
    m_a(List,[H2|T2]),
    H1 is 2 * H2,
    double(T2, T1).

I'll try to explain how I thought it would work. I flatten the first list and split it up into a head and a tail (H2 and T2). I split the second list into a head and a tail (H1 and T1). I check to make sure that H1 (the doubled value) is equal to 2 times H2 (the original value). If it is then I check the rest of the list. Eventually if they all match correctly I should be left with two empty lists which should match the first clause and return yes.

It works when there is only a single value (for example: double([[1]], X)). Can anyone offer any insight into what I am doing wrong? Is my logic or code incorrect?

+1  A: 

Your problem is that T2 is a single list so List after the recursive call is not a list of lists.

To solve this you can first use mega_append to flatten the list and then use an auxiliary predicate to work on the flattened list. I.e. the double will look like this:

double([],[]).
double(List,X) :-
    m_a(List,FList),
    double_aux(List, FList).

Edit: Here is a way to only use one clause since you want to see one. I recommend using an auxiliary predicate.

double([],[]).
double([[]],[]).
double(List,[H1|T1]) :-
    mega_append(List,[H2|T2]),
    H1 is 2 * H2,
    double([T2], T1).
Zeriab
Thank you. I was able to figure it out using that method. I'm still convinced there exists a way to do it using only one set of clauses though.
birderic
There is often a `flatten/2` procedure which does what your `m_a/2` does.
rvirding
There does indeed. I have edited my post with an example, but imo using an auxiliary predicate is better than that example. (There might be some nice way which I don't see atm.)
Zeriab
@rvirding: It does not work the same. If you replace m_a with flatten in the original code you'll say that it works. I.e. flatten(L1,L2), L1 = L2 when L1 is flat.
Zeriab
OK, `flatten` does allow a wider range of input than `mega_append`. You could just call it `append/2` and let the different number of arguments differentiate between appending 2 lists or a list of lists.
rvirding
It is good that you mention `flatten/2` as it is definitely something worth knowing about. At the same time this is homework and I have a suspicion that `mega_append` was crafted as a less powerful version of `flatten` to particularly prevent the first attempt by OP. Even with `flatten` I would create a predicate which solves the problem for a flattened list and then solve it for the more general case using that version. You know, problem decomposition. Originally I also tried to fit everything in a single predicate, but sometimes it's better to break the problem up.
Zeriab