diff --git a/config.json b/config.json index 345c84aa..34d89c7f 100644 --- a/config.json +++ b/config.json @@ -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" + ] } ] } diff --git a/exercises/list-ops/rebar.config b/exercises/list-ops/rebar.config new file mode 100644 index 00000000..db5d9076 --- /dev/null +++ b/exercises/list-ops/rebar.config @@ -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]}. diff --git a/exercises/list-ops/src/example.erl b/exercises/list-ops/src/example.erl new file mode 100644 index 00000000..ca735479 --- /dev/null +++ b/exercises/list-ops/src/example.erl @@ -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]). diff --git a/exercises/list-ops/src/list_ops.app.src b/exercises/list-ops/src/list_ops.app.src new file mode 100644 index 00000000..1799f5d5 --- /dev/null +++ b/exercises/list-ops/src/list_ops.app.src @@ -0,0 +1,9 @@ +{application, list_ops, + [{description, "exercism.io - list-ops"}, + {vsn, "0.0.1"}, + {modules, []}, + {registered, []}, + {applications, [kernel, + stdlib]}, + {env, []} + ]}. diff --git a/exercises/list-ops/src/list_ops.erl b/exercises/list-ops/src/list_ops.erl new file mode 100644 index 00000000..8f2a443a --- /dev/null +++ b/exercises/list-ops/src/list_ops.erl @@ -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. diff --git a/exercises/list-ops/test/list_ops_tests.erl b/exercises/list-ops/test/list_ops_tests.erl new file mode 100644 index 00000000..abec120d --- /dev/null +++ b/exercises/list-ops/test/list_ops_tests.erl @@ -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()).