Skip to content

Commit

Permalink
Implement list ops exercise
Browse files Browse the repository at this point in the history
Completes #192
  • Loading branch information
bgottlob committed Oct 15, 2018
1 parent f4a716d commit 8c9b0a2
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 0 deletions.
10 changes: 10 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,16 @@
"games",
"parsing"
]
},
{
"slug": "list-ops",
"uuid": "e80410bb-45e5-4007-ad97-e7470dd87ed5",
"core": false,
"unlocked_by": "rna-transcription",
"difficulty": 2,
"topics": [
"lists"
]
}
]
}
52 changes: 52 additions & 0 deletions exercises/list-ops/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# List Ops

Implement basic list operations.

In functional languages list operations like `length`, `map`, and
`reduce` are very common. Implement a series of basic list operations,
without using existing functions.

## Running tests

In order to run the tests, issue the following command from the exercise
directory:

For running the tests provided, `rebar3` is used as it is the official build and
dependency management tool for erlang now. Please refer to [the tracks installation
instructions](http://exercism.io/languages/erlang/installation) on how to do that.

In order to run the tests, you can issue the following command from the exercise
directory.

```bash
$ rebar3 eunit
```

### Test versioning

Each problem defines a macro `TEST_VERSION` in the test file and
verifies that the solution defines and exports a function `test_version`
returning that same value.

To make tests pass, add the following to your solution:

```erlang
-export([test_version/0]).

test_version() ->
1.
```

The benefit of this is that reviewers can see against which test version
an iteration was written if, for example, a previously posted solution
does not solve the current problem or passes current tests.

## Questions?

For detailed information about the Erlang track, please refer to the
[help page](http://exercism.io/languages/erlang) on the Exercism site.
This covers the basic information on setting up the development
environment expected by the exercises.

## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
30 changes: 30 additions & 0 deletions exercises/list-ops/rebar.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
%% Erlang compiler options
{erl_opts, [debug_info, warnings_as_errors]}.

{deps, [{erl_exercism, "0.1.2"}]}.

{dialyzer, [
{warnings, [underspecs, no_return]},
{get_warnings, true},
{plt_apps, top_level_deps}, % top_level_deps | all_deps
{plt_extra_apps, []},
{plt_location, local}, % local | "/my/file/name"
{plt_prefix, "rebar3"},
{base_plt_apps, [stdlib, kernel, crypto]},
{base_plt_location, global}, % global | "/my/file/name"
{base_plt_prefix, "rebar3"}
]}.

%% eunit:test(Tests)
{eunit_tests, []}.
%% Options for eunit:test(Tests, Opts)
{eunit_opts, [verbose]}.

%% == xref ==

{xref_warnings, true}.

%% xref checks to run
{xref_checks, [undefined_function_calls, undefined_functions,
locals_not_used, exports_not_used,
deprecated_function_calls, deprecated_functions]}.
54 changes: 54 additions & 0 deletions exercises/list-ops/src/example.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
-module(example).

-export([append/2, concat/1, filter/2, length/1, map/2, foldl/3, foldr/3,
reverse/1]).

append(L1, L2) -> L1 ++ L2.

concat(L) ->
concat(reverse(L), []).

concat([], Acc) -> Acc;
concat([Curr|Rest], Acc) ->
concat(Rest, extract_sublist(reverse(Curr), Acc)).

extract_sublist([], Acc) -> Acc;
extract_sublist([Curr|Rest], Acc) -> extract_sublist(Rest, [Curr|Acc]).

filter(Fun, L) ->
filter(Fun, L, []).

filter(_, [], Acc) -> reverse(Acc);
filter(Fun, [Curr|Rest], Acc) ->
case Fun(Curr) of
true -> filter(Fun, Rest, [Curr|Acc]);
false -> filter(Fun, Rest, Acc)
end.

length(L) -> length(L, 0).

length([], Acc) -> Acc;
length([_|Rest], Acc) -> length(Rest, Acc + 1).

map(Fun, L) ->
map(Fun, L, []).

map(_, [], Acc) -> reverse(Acc);
map(Fun, [Curr|Rest], Acc) -> map(Fun, Rest, [Fun(Curr)|Acc]).

foldl(Fun, Start, L) -> reduce_foldl(Fun, L, Start).
reduce_foldl(_, [], Acc) -> Acc;
reduce_foldl(Fun, [Curr|Rest], Acc) ->
reduce_foldl(Fun, Rest, Fun(Acc, Curr)).

foldr(Fun, Start, L) -> reduce_foldr(Fun, reverse(L), Start).

reduce_foldr(_, [], Acc) -> Acc;
reduce_foldr(Fun, [Curr|Rest], Acc) ->
reduce_foldr(Fun, Rest, Fun(Curr, Acc)).

reverse(L) ->
reverse(L, []).

reverse([], Acc) -> Acc;
reverse([Curr|Rest], Acc) -> reverse(Rest, [Curr|Acc]).
9 changes: 9 additions & 0 deletions exercises/list-ops/src/list_ops.app.src
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{application, list_ops,
[{description, "exercism.io - list-ops"},
{vsn, "0.0.1"},
{modules, []},
{registered, []},
{applications, [kernel,
stdlib]},
{env, []}
]}.
22 changes: 22 additions & 0 deletions exercises/list-ops/src/list_ops.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
-module(list_ops).

-export([append/2, concat/1, filter/2, length/1, map/2, foldl/3, foldr/3,
reverse/1, test_version/0]).

append(_List1, _List2) -> undefined.

concat(_List) -> undefined.

filter(_Function, _List) -> undefined.

length(_List) -> undefined.

map(_Function, _List) -> undefined.

foldl(_Function, _Start, _List) -> undefined.

foldr(_Function, _Start, _List) -> undefined.

reverse(_List) -> undefined.

test_version() -> 1.
86 changes: 86 additions & 0 deletions exercises/list-ops/test/list_ops_tests.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
-module(list_ops_tests).

-include_lib("erl_exercism/include/exercism.hrl").
-include_lib("eunit/include/eunit.hrl").

append_empty_lists_test() ->
?assertEqual([],
list_ops:append([], [])).

append_empty_list_to_list_test() ->
?assertEqual([1,2,3,4],
list_ops:append([], [1,2,3,4])).

append_non_empty_lists_test() ->
?assertEqual([1,2,2,3,4,5],
list_ops:append([1,2], [2,3,4,5])).

concat_empty_list_test() ->
?assertEqual([],
list_ops:concat([])).

concat_list_of_lists_test() ->
?assertEqual([1,2,3,4,5,6],
list_ops:concat([[1,2], [3], [], [4,5,6]])).

concat_list_of_nested_lists_test() ->
?assertEqual([[1], [2], [3], [], [4,5,6]],
list_ops:concat([[[1], [2]], [[3]], [[]], [[4,5,6]]])).

filter_empty_list_test() ->
?assertEqual([],
list_ops:filter(fun(X) -> X rem 2 =:= 1 end, [])).

filter_non_empty_list_test() ->
?assertEqual([1,3,5],
list_ops:filter(fun(X) -> X rem 2 =:= 1 end, [1,2,3,5])).

length_empty_list_test() ->
?assertEqual(0,
list_ops:length([])).

length_non_empty_list_test() ->
?assertEqual(4,
list_ops:length([1,2,3,4])).

map_empty_list_test() ->
?assertEqual([],
list_ops:map(fun(X) -> X + 1 end, [])).

map_non_empty_list_test() ->
?assertEqual([2,4,6,8],
list_ops:map(fun(X) -> X + 1 end, [1,3,5,7])).

foldl_empty_list_test() ->
?assertEqual(2,
list_ops:foldl(fun(X,Y) -> X * Y end, 2, [])).

foldl_direction_independent_function_applied_to_non_empty_list_test() ->
?assertEqual(15,
list_ops:foldl(fun(X,Y) -> X + Y end, 5, [1,2,3,4])).

foldl_direction_dependent_function_applied_to_non_empty_list_test() ->
?assertEqual(0,
list_ops:foldl(fun(X,Y) -> X div Y end, 5, [2,5])).

foldr_empty_list_test() ->
?assertEqual(2,
list_ops:foldr(fun(X,Y) -> X * Y end, 2, [])).

foldr_direction_independent_function_applied_to_non_empty_list_test() ->
?assertEqual(15,
list_ops:foldr(fun(X,Y) -> X + Y end, 5, [1,2,3,4])).

foldr_direction_dependent_function_applied_to_non_empty_list_test() ->
?assertEqual(2,
list_ops:foldr(fun(X,Y) -> X div Y end, 5, [2,5])).

reverse_empty_list_test() ->
?assertEqual([],
list_ops:reverse([])).

reverse_non_empty_list_test() ->
?assertEqual([7,5,3,1],
list_ops:reverse([1,3,5,7])).

version_test() -> ?assertMatch(1, list_ops:test_version()).

0 comments on commit 8c9b0a2

Please sign in to comment.