-2

I am a beginner in Prolog and I am stuck in a homework assignment. I have to build a predicate myReverse(XS,YS) where XS is the reverse list of YS. I have built some logic as follows:

myReverse([],[]).
myReverse([H],[H]).
myReverse([L|H],[H|T]) :- myReverse(L,T).

This kinda works, but the output is not quite what I want. Some examples:

myReverse(L,[]).
L = []                  % ok, that's fine

myReverse(L,[a]).
L = [a]                 % still fine

myReverse(L,[a,b]).
L = [[b]|a]             % expected [b,a]

myReverse(L,[a,b,c]).
L = [[[c]|b]|a]         % expected [c,b,a]

...

Is there any way I can achieve the expected output without using a accumulator or third party implementations like append?

Edit: This question is NOT a duplicate of Reversing a List in Prolog because I do NOT want to use accumulator. Also, this question is much more about the output format of my given solution than the solution itself.

Diego
  • 374
  • 1
  • 2
  • 14
  • please check my answer. – TaQuangTu Jul 04 '19 at 03:51
  • 1
    Possible duplicate of [Reversing a List in Prolog](https://stackoverflow.com/questions/19471778/reversing-a-list-in-prolog) – Daniel Lyons Jul 04 '19 at 04:47
  • You are asking "how to implement `reverse/2` in Prolog" as if you couldn't find a solution using google (or another search engine) or even a textbook. You are notably **not** asking "how is my approach wrong". This is the kind of question that tends to attract negative feedback, which in turn might make you believe that Stackoverflow is a stupid place where rude know-it-alls abuse beginners. – User9213 Jul 04 '19 at 09:58
  • @DanielLyons This question is not a duplicate of "Reversing a List in Prolog" as I obviously did some really deep research before asking it here. In that question the upvoted answer suggests the use of accumultor, which I clearly do not want to, as said in my question. – Diego Jul 04 '19 at 14:06
  • "Some really deep research" yeah. I did a google search with "prolog reverse without accumulator" and found a Stackoverflow question and answers in less than 3 minutes. You talk about "output format" but actually, it is not the format that is wrong in your attempt, it is that it is not a reversed list, it is a nested structure of some kind. It is definitely not a reversed list. The really interesting question here is, "what does it mean to reverse a list in Prolog without an accumulator", and for that, you might need to understand what a list is, and so on. – User9213 Jul 04 '19 at 14:19
  • @User9213 You did find a reverse without accumulator? So why did you not correct my implementation to fit the objective of the question? I'm not asking for a implementation from scratch, as I provided my own. Instead you're being rude and not pointing the mistakes. Also, you have been rude on the answer (which I haven't evaluated yet because I do not have my computer avaliable now) provided by Ta Quand Tu, which seems a valid approach to my problem. – Diego Jul 04 '19 at 14:27
  • https://stackoverflow.com/a/28844480/10721357 There you go, no need to say thank you. – User9213 Jul 04 '19 at 14:29
  • Your implementation **does not reverse a list**. It does something but it is not a list reversal. I hope at least this reaches the part of your brain that is not too busy getting insulted over nothing. – User9213 Jul 04 '19 at 14:30
  • The approach by @TaQuangTu is using `append`, whether you want to believe that or not. – User9213 Jul 04 '19 at 14:32
  • The syntax `[H|T]` divides a list into an element `H` and a list `T`. When swap these around as `[T|H]` (or `[H|T]` in your second clause) you are trying to say the first element of the list is the list `T` and the rest of the list is the element `H`. What you are thinking of as an "output formatting" issue is actually that you are incorrectly building your result list. You're actually making a very large "improper" list, so named because the tail of the cons cells are not lists. – Daniel Lyons Jul 05 '19 at 06:35
  • I don't believe there is an efficient accumulator-less, two-argument, non-appending version of `reverse/2`. You can find other implementations that satisfy one or some of these constraints but not all of them. @User9213 found one that meets most but is really inefficient. The [DCG solution](https://stackoverflow.com/a/19472734/812818) is really elegant, but under the hood there are several extra parameters as it's basically using difference lists. It's very efficient though. – Daniel Lyons Jul 05 '19 at 06:41
  • @DanielLyons Well, I doubt that OP will go that way, but I think you can _prove formally_ that it isn't possible to do it. The solution I linked, for example, has a hidden `append` in it. – User9213 Jul 05 '19 at 12:27

1 Answers1

0

You've misunderstood the [H|T] notation, tracing through the example reverse(L, [a, b]) will reveal it.

The first unification:

rule: myReverse([L|H],[H|T]) :- myReverse(L,T).
unified: myReverse([L|a], [a|[b]]) :- myReverse(L, [b]).

At this point a is a single atom, not a list, the T in [H|T] needs to be a list for a list to be complete. [a, b, c] is syntactic sugar for .(a, .(b, .(c, []))).

Your second unification, and third unifications are:

second: myReverse([b], [b]).
going back: myReverse([[b]|a], [a|[b]]) :- myReverse([b], [b]).
which outputs as: myReverse([[b]|a], [a, b]).

Your output [[b]|a] is not a complete list because a was an atom, not a list, which is why it's not output as [[b], a].

To not use an accumulator you'll need to add elements to the list as you exit the recursion and go back through the stack frames, keeping in mind a when you're handling a list or an atom:

myReverse([], []). % base case
myReverse([Head|Tail], Reversed) :-
    myReverse(Tail, TailReversed), % recursive call
    add_to_tail(Head, TailReversed, Reversed). % Put Head onto the end

add_to_tail(X, [],[X]). % base case
add_to_tail(X, [H|T], [H|Out]) :- 
    add_to_tail(X, T, Out). % recursive call, work done on exit

It's more efficient to use an accumulator. Best of luck learning Prolog.

Paul Brown
  • 2,235
  • 12
  • 18