name = 'last_elem/2' slug = 'find last element in list' description = '''\
last_elem(L, E)
: E
is the last element of list L
.
?- last_elem([1,2,3], X). X = 3. ?- last_elem([3,2,X], 1). X = 1.''' plan = ['''\
It's easy to access the first element in a list, but to get to the last element one needs to recursively go through the whole list.
''', '''\The list can be divided into its head and tail, and the search can proceed with the tail. The problem is now smaller (the tail is shorter than the whole list), so we can use recursion.
''', '''\If X
is the last element of tail T
, then X
is also
the last element of the whole list that looks like [H|T]
.
The operator ==
is "stricter" than operator =
in the sense that
for the latter it is enough to be able to make the two operands equal (unification). Perhaps by using =
you can make the predicate last_elem/2
more general (e.g. able to work with output arguments becoming inputs).
Of course, you can also solve the exercise without explicit use of either of these two operators, just remember that unification is implicitly performed with the predicate's arguments (head of clause).
''', 'eq_instead_of_equ_markup': '''\Perhaps the operator for unification (=) would be better?
''', 'base_case': '''\Did you think of a base case? What's the simplest possible case? What if the list contains only one element?
''', '[]_should_not_succeed': '''\How did you succeed to find a last element in an empty list? You likely need a different base case.
''', 'list_returned': '''\You are returning a list instead of an element.
''', 'clumsy_conc_use': '''\Are you using conc/3
? An interesting idea. Don't forget that the second list you're
concatenating must be of length one if you want to achieve the desired effect.
So a pattern of the form [X]
, right?
Are you using conc/3
? An interesting idea; it is possible to solve in this way.
However, a bit of tweaking is still needed. Don't forget that conc/3
has three arguments,
and all three are lists. Think about what kind of a pattern do you need...
The base case is ok. However, what about the general recursive case?
''', 'predicate_always_false': '''\It seems your predicate is
If the name is correct, check whether something else is misspelled, perhaps there is a full stop instead of a comma or vice versa, or maybe you typed a variable name in lowercase?
It is, of course, also possible that your conditions are too restrictive, or even impossible to satisfy
(as would be, for example, the condition that an empty list []
is equal to a list with
exactly three elements [A,B,C]
,
or something similarly impossible).
Is there an infinite recursion at work here? How will it ever stop?
Or perhaps is there a missing, faulty, or simply incompatible (with the general recursive case) base case?
''', 'final_hint': '''\Interesting fact: predicate conc/3
can be used to search for patterns in lists. The last
element in a list is also a kind of pattern. What happens if we concatenate an arbitrary list _
and a list of length one (in this order)? A list of length one is of course written as
[Element]
.
Try asking the following query:
?- conc(_, [Element], [a,b,c,d,e,f,q]).
So, can you now fetch the list's last element using conc/3
? This will be very useful in
further exercises. On the other hand, of course, accessing the last element of a list is still quite
expensive, it's done in O(n) time. Therefore, if it's not important which element of a list is to be used,
or where in a list a new element is to be added, always work with the head.
And what does this query do? ;)
?- conc([a,b,c], [q], L).