I have a task: "Add second and fifth elements of list to the end of the list and remove the penultimate element". I need to do this without using the module lists
, just using recursion.
I have code which finds an element by index, which solves the problem of finding the second and fifth element (also not an ideal solution for me, because it's the same lists:nth
function).
-module(task).
-export([remove_and_add/1]).
remove_and_add(List) ->
remove_penultimate(List) ++ [nth(2, List)] ++ [nth(5, List)].
nth(1, [H|_]) ->
H;
nth(N, [_|T]) ->
nth(N - 1, T).
But I don't understand how to remove the penultimate element without a length (how to implement remove_penultimate function).
remove_penultimate(List) ++ [nth(2, List)] ++ [nth(5, List)].
Only intermediate to advanced erlang programmers know how to use ++
correctly. I suggest you completely ignore it and omit using it in any of your code.
If you can't use the lists module, then the first function every beginner needs to write is a reverse(List)
function. Beside being one of the most useful functions, that will teach you the trick of how to add an "accumulator" variable to a function's parameter variables to store whatever data you want in it. Here is an example:
go(List) ->
go(List, []).
go([H|T], Acc) -> %% Acc starts off as a blank list, which can be used to store data
%% maybe do something here
go(T, [H|Acc]); %% storing data in the Acc list
go([], Acc) -> Acc.
You can add as many variables as you need to a function's parameters using the trick above, for instance:
go(List) ->
go(List, 1, none, none, []).
go([H|T], N, X, Y, Acc) -> ...
Examine this function:
show_previous(List) ->
show_previous(List, none).
show_previous([], Prev) -> %% A variable called Prev has been added to the function's parameters, and it starts off with the value none.
io:format("current: end of list, previous: ~w~n", [Prev]);
show_previous([Last], Prev) -> %% This clause only matches a list with one element.
io:format("current: ~w, penultimate: ~w~n", [Last, Prev]),
show_previous([H|T], Prev) ->
io:format("current: ~w, prev: ~w~n", [H, Prev]),
show_previous(T, H).
Note that in erlang, a function with the same name but with a different number of parameter variables (known as the "arity" of a function) is a completely different function, so show_previous/1
and show_previous/2
are completely different functions.
Here's another example of what you can do:
show([]) ->
io:format("!No more elements!~n");
show([X, Y]) -> %% only matches a list with two elements
io:format("penultimate: ~w, last: ~w~n", [X, Y]),
show([Y]);
show([H|T]) ->
io:format("current: ~w~n", [H]),
show(T). %% By whittling the list down one element at a time,
%% the list will eventually become a 2 element list
Finally, here is an example that shows how powerful pattern matching is in erlang:
show(List) ->
show(List, 1).
show([H|T], 3) ->
io:format("The third element in the list is: ~w~n", [H]),
show(T, 4);
show([H|T], Index) ->
io:format("The element at index positon ~w is ~w~n", [Index, H]),
show(T, Index+1);
show([], _Index) ->
ok.
In erlang, a function's parameters can contain constants, like 3
or the atom hello
or the list [1, 2, 3]
, while in languages like python or ruby, all the parameters in a function must be variables.
But I don't understand how to remove the penultimate element without a length.
You could always get the length by recursing over all the elements and counting them. It would be good practice to write your own length()
function. It's very simple. Remember though, it's more efficient to recurse over a list as few times as possible, and you should be able to solve your original problem by recursing over the list once, then reversing the list (which requires recursing over the list again).
Another possible approach to removing the penultimate element is to reverse the list, then ask yourself, "How do I remove the second element of a list?"
I came up with a solution that traverses the original list once, and when it comes to the end of the list, it reverses the Acc
list and returns it. It takes this list:
[1, 2, 3, 4, 5, 6, 7]
and returns this list:
[1,3,4,7,2,5]
I would describe that operation as removing the penultimate element in the list and moving the 2nd and 5th elements to the end of the list. Copying the 2nd and 5th elements and adding the copies to the end of the list would be solved in a similar fashion.