1

This is the command that calls the procedure add1:

listmanager::add1([1,2,3,4], NewList),

please note the NewList variable is unbound.

The following add1 procedure adds 1 to each element of the input list [1,2,3,4]. It stores the information in the output list NewList.

clauses:

add1([], []).            /* boundary condition */
add1([Head|Tail], [NewHead|NewTail]):- 
    /* separate the head from the rest of the list */
    NewHead = Head+1,    /* add 1 to the first element */
    add1(Tail, NewTail). /* call element with the rest of the list */

There is no explicit assignment of NewTail and NewHead to the output list.

I ran this code through the debugger. It works. The head of the input list is bound and 1 is added. In the last step, add1([],[]), the output list has all the elements +1 of the input list.

How?

Will Ness
  • 70,110
  • 9
  • 98
  • 181

2 Answers2

1

There is no explicit assignment of NewTail and NewHead to the output list.

There are no assignments at all, Prolog uses unification. This is important, because unification plays the role of both analyzing existing data structures and building new ones. This is different from programming languages that have assignments.

It is also important to know that putting terms inside a clause head is an implicit unification. So this:

add1([Head|Tail],[NewHead|NewTail]) :-
    ...

is equivalent to this:

add1(List1, List2):-
    List1 = [Head | Tail],
    List2 = [NewHead | NewTail],
    ...

(But the first form can sometimes be executed more efficiently.)

Now, for the two roles of unification, consider this predicate:

list_head_tail([Head | Tail], Head, Tail).

which can also be written as:

list_head_tail(List, Head, Tail) :-
    List = [Head | Tail].

Due to the properties of unification, this can work in different ways. We can use it to "construct" a list:

?- list_head_tail(List, a, [b, c]).
List = [a, b, c].

but also to "deconstruct" an existing list:

?- List = [1, 2, 3], list_head_tail(List, Head, Tail).
List = [1, 2, 3],
Head = 1,
Tail = [2, 3].

Note that this binds Head and Tail to values even though "there is no explicit assignment" of either of these variables in the body of list_head_tail/3 predicate. Unification can bind variables to values in this way.

So to answer your question, when you call add1([1, 2, 3, 4], NewList) the following things happen to NewList:

  1. it is unified with [NewHead | NewTail]
  2. NewHead is unified with a new value; if you use is for arithmetic, NewHead is unified with 2, so the value of NewList is now [2 | NewTail]
  3. in the recursive call to add1, NewTail is unified with a new term [NewHead2 | NewTail2] (I'm using renamed (freshly named) variables here as Prolog itself in effect does)
  4. NewHead2 is unified with 3, so your outermost (or topmost) NewList is now [2 | [3 | NewTail2]] = [2, 3 | NewTail2]
  5. some more recursive calls to add1 happen, progressively setting up NewHead3 and NewTail3, NewHead4 and NewTail4, etc. -- I'll skip them here
  6. in the final recursive call, the last NewTail_n variable is unified with [] and the chain of add1 calls is thus finished
  7. at this point NewList = [2 | [3 | [4 | [5 | []]]]], constructed "from the outside in" (i.e. top-down); this term is equivalent to the more readable syntax [2, 3, 4, 5]
Will Ness
  • 70,110
  • 9
  • 98
  • 181
Isabelle Newbie
  • 9,258
  • 1
  • 20
  • 32
0

That is because in Prolog 1+1 is just 1+1 (or more canonical +(1,1)), not 2. You can use is/2 [swi-doc] to evaluate a numerical expression, so:

add1([], []).
add1([Head|Tail],[NewHead|NewTail]):-
    NewHead is Head+1,
    add1(Tail, NewTail).

You can furthermore make use of maplist/3 to perform a mapping:

inc(X, Y) :-
    Y is X+1.

add1(L, R) :-
    maplist(inc, L, R).
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • 1
    There is no question about adding 1 to each element. The question is NewTail is not assigned. How is it getting bound to the elements + 1 from the input list? Looad at the second lin add1([Head|Tail], [NewHead|NewTail]) :- .... Head and Tail are assigned from the list[ [1,2,3,4]. Where is {NewHead|NewTail getting bound? – Irongrinder Jun 27 '20 at 16:50
  • @Irongrinder: `NewTail` is grounded by the recursive call in `add1`, it will keep unifying it with a new "cons" until it fires `add1([],[])`, when it will be unfied with `[]`. – Willem Van Onsem Jun 27 '20 at 17:16