Last active
August 5, 2020 20:30
-
-
Save sgobotta/3bda705438d790733f7993f8ed05bdd1 to your computer and use it in GitHub Desktop.
Gist for the FeatureLearn Erlang course (4.12) "Strategies exercises"
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(rps). | |
-author("Santiago Botta <[email protected]>"). | |
-include_lib("eunit/include/eunit.hrl"). | |
-export([ | |
play/1, play_two/3, val/1,tournament/2,const/2,enum/1,get_strategies/0, | |
no_repeat/1,rock/1,cycle/1,rand/1,echo/1,least_frequent/1,most_frequent/1, | |
random_strategy/1, best_scored/1, | |
main_test_game/0 | |
]). | |
-import(utils, [least_frequents/1,most_frequents/1,take/2]). | |
-type play() :: rock | paper | scissors. | |
-type outcome() :: win | lose | draw. | |
-type strategy() :: function(). | |
-type strategy_name() :: rock | echo | no_repeat | cycle | rand | echo | least_frequent | most_frequent | random_strategy | best_scored. | |
-type strategy_score() :: {rock | echo | no_repeat | cycle | rand | echo | least_frequent | most_frequent | random_strategy | best_scored, integer()}. | |
%% @doc Runs a test game where PlayerL uses a best_scored strategy, a rand | |
%% strategy for PlayerR and 420 rounds are played. | |
-spec main_test_game() -> ok. | |
main_test_game() -> | |
play_two(best_scored(maps:to_list(get_strategies())), fun rand/1, 420). | |
%% @doc Plays one strategy against another, for N moves. | |
-spec play_two(strategy(), strategy(), integer()) -> ok. | |
play_two(StrategyL,StrategyR,N) -> | |
print_play_two_header(integer_to_list(N)), | |
play_two(StrategyL,StrategyR,[],[],N,1,0). | |
%% @doc Given two strategies, two list of moves, a maximum number of rounds, a | |
%% current round and a score, plays rps using Tail recursive loop for play_two/3. | |
-spec play_two(strategy(), strategy(), [play()], [play()], integer(), integer(), integer()) -> ok. | |
play_two(_,_,_PlaysL,_PlaysR,0,_RoundN,Score) -> | |
print_overall_result(Score); | |
play_two(_StrategyL,_StrategyR,_PlaysL,_PlaysR,N,_RoundN,Score) when ((Score + N) < 0) -> | |
print_overall_result(Score); | |
play_two(_StrategyL,_StrategyR,_PlaysL,_PlaysR,N,_RoundN,Score) when ((Score - N) > 0) -> | |
print_overall_result(Score); | |
play_two(StrategyL,StrategyR,PlaysL,PlaysR,N,RoundN,Score) -> | |
PlayL = StrategyL(PlaysR), | |
PlayR = StrategyR(PlaysL), | |
PlayResult = result(PlayL,PlayR), | |
print_play(PlayL,PlayR,RoundN,print_play_result(PlayResult)), | |
play_two(StrategyL, StrategyR, [PlayL|PlaysL], [PlayR|PlaysR], N-1, RoundN+1,Score+outcome(PlayResult)). | |
%% @doc Interactively play against a strategy, provided as argument. | |
-spec play(strategy()) -> ok. | |
play(Strategy) -> | |
print_play_header(), | |
io:format("Rock - paper - scissors~n"), | |
io:format("Play one of rock, paper, scissors, ...~n"), | |
io:format("... r, p, s, stop, followed by '.'~n"), | |
play(Strategy,[],[],1). | |
%% @doc Given a strategy, a list of PlayerL plays, a list of Player plays and | |
%% a round number, plays a rps game using a tail recursive loop for play/1. | |
-spec play(strategy(), [play()], [play()], integer()) -> ok. | |
play(Strategy,Moves,OpponentMoves,RoundN) -> | |
{ok,P} = io:read("Play: "), | |
Play = expand(P), | |
case Play of | |
stop -> | |
io:format("Stopped~n~n"), | |
print_overall_result(tournament(Moves, OpponentMoves)); | |
_ -> | |
OpponentMove = Strategy(Moves), | |
print_play(Play, OpponentMove, RoundN, print_play_result(result(Play,OpponentMove))), | |
play(Strategy,[Play|Moves],[OpponentMove|OpponentMoves],RoundN+1) | |
end. | |
%% @doc Given a play() returns a character representation. | |
-spec get_unicode(play()) -> string(). | |
get_unicode(rock) -> "✊"; | |
get_unicode(paper) -> "✋"; | |
get_unicode(scissors) -> "🤞". | |
% | |
% Auxiliary functions | |
% | |
%% @doc Transforms shorthand atoms to expanded form. | |
-spec expand(atom()) -> play(). | |
expand(r) -> rock; | |
expand(p) -> paper; | |
expand(s) -> scissors; | |
expand(X) -> X. | |
% @doc Returns the result of one set of plays. | |
-spec result(play(), play()) -> outcome(). | |
result(rock,rock) -> draw; | |
result(rock,paper) -> lose; | |
result(rock,scissors) -> win; | |
result(paper,rock) -> win; | |
result(paper,paper) -> draw; | |
result(paper,scissors) -> lose; | |
result(scissors,rock) -> lose; | |
result(scissors,paper) -> win; | |
result(scissors,scissors) -> draw. | |
%% @doc Returns the result of a tournament (list of plays). | |
-spec tournament([play()], [play()]) -> integer(). | |
tournament(PlaysL,PlaysR) -> | |
lists:sum( | |
lists:map(fun outcome/1, | |
lists:zipwith(fun result/2,PlaysL,PlaysR))). | |
%% @doc Transforms a round outcome to a number, representing a positive number | |
%% for plays won by the first player and a negative number for the second | |
%% player. | |
-spec outcome(outcome()) -> integer(). | |
outcome(win) -> 1; | |
outcome(lose) -> -1; | |
outcome(draw) -> 0. | |
%% @doc Transforms 0, 1, 2 to rock, paper, scissors. | |
-spec enum(integer()) -> play(). | |
enum(0) -> | |
rock; | |
enum(1) -> | |
paper; | |
enum(2) -> | |
scissors. | |
%% @doc Transforms rock, paper, scissors to 0, 1, 2. | |
-spec val(play()) -> integer(). | |
val(rock) -> | |
0; | |
val(paper) -> | |
1; | |
val(scissors) -> | |
2. | |
%% @doc Gives the play which the argument beats. | |
-spec beats(play()) -> play(). | |
beats(rock) -> | |
scissors; | |
beats(paper) -> | |
rock; | |
beats(scissors) -> | |
paper. | |
%% @doc Returns the play the argument looses by | |
-spec loose(play()) -> play(). | |
loose(rock) -> | |
paper; | |
loose(paper) -> | |
scissors; | |
loose(scissors) -> | |
rock. | |
% | |
% strategies. | |
% | |
%% @doc Returns a list of strategies | |
-spec get_strategies() -> map(). | |
get_strategies() -> | |
#{ | |
echo => fun echo/1, | |
rock => fun rock/1, | |
no_repeat => fun no_repeat/1, | |
cycle => fun cycle/1, | |
rand => fun rand/1, | |
least_frequent => fun least_frequent/1, | |
most_frequent => fun most_frequent/1 | |
}. | |
%% @doc Given a list of moves echoes the last opponent's move. | |
-spec echo([play()]) -> play(). | |
echo([]) -> | |
paper; | |
echo([Last|_]) -> | |
Last. | |
%% @doc Doesn't matter the opponent's move, will always play rock. | |
-spec rock([play()]) -> play(). | |
rock(_) -> | |
rock. | |
%% @doc Given a list of moves returns a move that beats the last opponent's play. | |
-spec no_repeat([play()]) -> play(). | |
no_repeat([]) -> | |
paper; | |
no_repeat([X|_]) -> | |
beats(X). | |
const(Play, _) -> | |
Play. | |
%% @doc Given a list of moves returns a move acoording to the number of moves. | |
%% played so far. | |
-spec cycle([play()]) -> play(). | |
cycle(Xs) -> | |
enum(length(Xs) rem 3). | |
%% @doc Given a list of moves returns a randomly taken play. | |
-spec rand([play()]) -> play(). | |
rand(_) -> | |
enum(rand:uniform(3) - 1). | |
%% @doc Given a list of moves returns the play that beats the opponent's least | |
%% used play. | |
-spec least_frequent([play()]) -> play(). | |
least_frequent(Xs) -> | |
modes(Xs, fun utils:least_frequents/1, fun beats/1). | |
%% @doc Given a list of moves returns the play that looses against the opponent's | |
%% most used play. | |
-spec most_frequent([play()]) -> play(). | |
most_frequent(Xs) -> | |
modes(Xs, fun utils:most_frequents/1, fun loose/1). | |
%% @doc Generic function used by most_frequent/1 and least_frequent/1 functions. | |
%% Given a list of moves, a function that finds the most frequent or least | |
%% frequent move and a beats/1 or loose/1 function returns a suitable play. | |
-spec modes([play()], function(), strategy()) -> play(). | |
modes([],_,_) -> | |
rand([]); | |
modes(Xs,Mode,ChoosePlay) -> | |
Modes = Mode(Xs ++ [paper,rock,scissors]), | |
% Modes always return a list of most/least frequent elements, therefore one | |
% is choosen randomly, when the algorith is certain, there will only be one | |
% element, and will always choose it. | |
ChoosePlay(lists:nth(rand:uniform(length(Modes)), Modes)). | |
%% @doc Given a non empty list of strategy returns a function that given a list | |
%% of plays chooses a strategy randomly. | |
-spec random_strategy([play()]) -> play(). | |
random_strategy(OpponentMoves) -> | |
Strategies = maps:to_list(get_strategies()), | |
{_, Strategy} = lists:nth(rand:uniform(length(Strategies)), Strategies), | |
Strategy(OpponentMoves). | |
%% @doc Given a list of strategies, returns a function that takes a list of | |
%% moves to return a move according to the best scored strategy. | |
-spec best_scored([play()]) -> strategy(). | |
best_scored(Strategies) -> | |
fun (OpponentMoves) -> | |
[StrategiesResultHead | StrategiesResultTail] = lists:map( | |
fun (StrategyName) -> | |
get_strategy_score(StrategyName, OpponentMoves) | |
end, | |
Strategies | |
), | |
{BestScoredStrategyName, _Score} = get_best_scored_strategy(StrategiesResultTail, StrategiesResultHead), | |
BestScoredStrategy = maps:get(BestScoredStrategyName, maps:from_list(Strategies)), | |
BestScoredStrategy(OpponentMoves) | |
end. | |
%% @doc Given a list of strategy_score(), returns the best scored strategy_score(). | |
-spec get_best_scored_strategy([strategy_score()], strategy_score()) -> strategy_score(). | |
get_best_scored_strategy([], Best) -> | |
Best; | |
get_best_scored_strategy([{_StrategyName, Score} = S |Xs], {_BestStrategy, BestScore} = B) -> | |
case Score > BestScore of | |
true -> get_best_scored_strategy(Xs, S); | |
false -> get_best_scored_strategy(Xs, B) | |
end. | |
get_best_scored_strategy_test() -> | |
StrategiesScores = [{rock, -2}, {echo, 2}, {least_frequent, -1}, {rand, 0}], | |
ExpectedResult = {echo, 2}, | |
?assertEqual(ExpectedResult, get_best_scored_strategy(tl(StrategiesScores), hd(StrategiesScores))). | |
%% @doc Given a strategy name with it's function and a list of moves, returns a | |
%% score. | |
-spec get_strategy_score({strategy_name(), strategy()}, [play()]) -> strategy_score(). | |
get_strategy_score({StrategyName, StrategyFunction}, OpponentMoves) -> | |
StrategyResults = test_strategy(StrategyFunction, OpponentMoves), | |
{StrategyName, tournament(StrategyResults, OpponentMoves)}. | |
get_strategy_score_test() -> | |
OpponentMoves = [paper, paper, paper], | |
RockStrategyName = {rock, fun rock/1}, | |
?assertEqual({rock, -3}, get_strategy_score(RockStrategyName, OpponentMoves)), | |
EchoStrategyName = {echo, fun echo/1}, | |
?assertEqual({echo, 0}, get_strategy_score(EchoStrategyName, OpponentMoves)). | |
%% @doc Given a strategy and a list of moves, returns a list of moves played by | |
%% the strategy. | |
-spec test_strategy(strategy(), [play()]) -> [play()]. | |
test_strategy(Strategy, Xs) -> | |
test_strategy(Strategy, lists:reverse(Xs), [], []). | |
%% @doc Given a strategy, a list of moves and two accumulators, one that | |
%% reconstructs the opponent moves and the other accumulates the strategy plays, | |
%% and returns the last accumulator. | |
%% The head of the list of moves should be the opponent's first movement, | |
%% meaning that in some cases the given list should sometimes be reversed. | |
-spec test_strategy(strategy(), [play()], [play()], [play()]) -> [play()]. | |
test_strategy(_Strategy, [], _OpponentMoves, Acc) -> | |
Acc; | |
test_strategy(Strategy, [X|Xs], OpponentMoves, Acc) -> | |
test_strategy(Strategy, Xs, [X|OpponentMoves], [Strategy(OpponentMoves) | Acc]). | |
test_strategy_using_rock_strategy_test() -> | |
OpponentMoves = [scissors,paper,rock], | |
?assertEqual( | |
[rock,rock,rock], | |
test_strategy(fun rock/1, OpponentMoves) | |
). | |
test_strategy_using_echo_strategy_test() -> | |
OpponentMoves = [scissors,paper,rock], | |
EchoDefaultMove = paper, | |
?assertEqual( | |
[paper,rock,EchoDefaultMove], | |
test_strategy(fun echo/1, OpponentMoves) | |
). | |
test_strategy_using_least_frequent_strategy_test() -> | |
OpponentMoves = [scissors,paper,rock,rock,rock,paper], | |
?assertEqual( | |
[paper,paper,paper,paper], | |
% We take the first 4 elements, because the first ones are randomly | |
% computed by the strategy. | |
utils:take(4, test_strategy(fun least_frequent/1, OpponentMoves)) | |
). | |
test_strategy_using_most_frequent_strategy_test() -> | |
OpponentMoves = [rock,paper,scissors,rock,paper,paper], | |
?assertEqual( | |
[scissors,scissors,scissors,scissors,scissors], | |
% We take the first 5 elements, because the first one is randomly | |
% computed by the strategy. | |
utils:take(5, test_strategy(fun most_frequent/1, OpponentMoves)) | |
). | |
% | |
% Print functions | |
% | |
-spec wrap_text(string(), integer(), atom()) -> [string()]. | |
wrap_text(Text, CellLength, Alignment) -> | |
["║", string:pad(Text, CellLength, Alignment), "║"]. | |
-spec print_row([string()], integer(), string(), string(), string()) -> [string()]. | |
print_row(Cells, N, FirstChar, ColumnSeparator, LastChar) -> | |
print_row(Cells, N, FirstChar, ColumnSeparator, LastChar, ""). | |
-spec print_row([string()], integer(), string(), string(), string(), [string()]) -> [string()]. | |
print_row([X|[]],N,FirstChar,_ColumnSeparator,LastChar,Acc) -> | |
[FirstChar, Acc, string:pad(X, N, both), LastChar]; | |
print_row([X|Xs],N,FirstChar,ColumnSeparator,Lastchar, Acc) -> | |
print_row(Xs, N, FirstChar, ColumnSeparator, Lastchar, Acc ++ string:pad(X, N, both) ++ ColumnSeparator). | |
-spec print_play_header() -> ok. | |
print_play_header() -> | |
StartHeader = ["╔", lists:duplicate(91, "═"), "╗\n"], | |
Title = [wrap_text("Rock, Paper, Scissors", 91, both), "\n"], | |
Hint = [wrap_text("Play one of rock, paper, scissors typing: r, p, s, stop, followed by '.'", 91, both), "\n"], | |
BlankLine = [wrap_text("", 91, trailing), "\n"], | |
EndHeader = ["╚", lists:duplicate(91, "═"), "╝\n"], | |
io:format("~ts", [[StartHeader, Title, BlankLine, Hint, BlankLine, EndHeader]]). | |
-spec print_play_two_header(integer()) -> ok. | |
print_play_two_header(RoundsNumber) -> | |
StartTable = ["╔", lists:duplicate(91, "═"), "╗\n"], | |
Title = [wrap_text("Rock, Paper, Scissors", 91, both), "\n"], | |
BlankLine = [wrap_text("", 91, trailing), "\n"], | |
Rounds = [wrap_text([RoundsNumber, " Rounds will be played."], 91, both), "\n"], | |
HeaderSDecoration = [print_row(lists:duplicate(4, lists:flatten(lists:duplicate(22, "═"))), 22, "╠", "╦", "╣"), "\n"], | |
HeaderEDecoration = [print_row(lists:duplicate(4, lists:flatten(lists:duplicate(22, "═"))), 22, "╚", "╩", "╝"), "\n"], | |
HeaderTitles = ["Round", "PlayerL", "PlayerR", "Round Winner"], | |
Header = [print_row(HeaderTitles, (88 div 4), "║", "║", "║"), "\n"], | |
io:format("~ts", [[StartTable, Title, BlankLine, Rounds, BlankLine, HeaderSDecoration, Header, HeaderEDecoration]]). | |
%% @doc Given a score, prints out the overall result. | |
-spec print_overall_result(integer()) -> ok. | |
print_overall_result(Score) -> | |
case Score of | |
0 -> print_result("It's a draw game."); | |
_ -> | |
case Score > 0 of | |
true -> print_result("Player L wins!"); | |
false -> print_result("Player R wins!") | |
end | |
end, | |
io:format("~nEnd of game.~n"). | |
-spec print_play(play(), play(), integer(), string()) -> ok. | |
print_play(PlayL, PlayR, RoundN, Result) -> | |
Message = [print_row([integer_to_list(RoundN), get_unicode(PlayL), get_unicode(PlayR), Result], (88 div 4), " ", " ", " "), "\n"], | |
io:format("~ts", [Message]). | |
-spec print_result(string()) -> ok. | |
print_result(Text) -> | |
StartTable = ["╔", lists:duplicate(91, "═"), "╗\n"], | |
Result = [wrap_text(Text, 91, both), "\n"], | |
EndTable = ["╚", lists:duplicate(91, "═"), "╝\n"], | |
io:format("~ts", [[StartTable, Result, EndTable, "\n"]]). | |
-spec print_play_result(outcome()) -> string(). | |
print_play_result(draw) -> | |
"Draw"; | |
print_play_result(win) -> | |
"PlayerL scores!"; | |
print_play_result(lose) -> | |
"PlayerR scores!". |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(rps_tests). | |
-author("sgobotta"). | |
-include_lib("eunit/include/eunit.hrl"). | |
-import(rps,[ | |
get_strategies/0, | |
no_repeat/1,cycle/1,least_frequent/1,most_frequent/1 | |
]). | |
%% Strategy tests | |
no_repeat_test() -> | |
?assertEqual(paper, rps:no_repeat([scissors])), | |
?assertEqual(scissors, rps:no_repeat([rock])), | |
?assertEqual(rock, rps:no_repeat([paper])). | |
cycle_test() -> | |
?assertEqual(rock, rps:cycle([])), | |
?assertEqual(paper, rps:cycle([dummy])), | |
?assertEqual(scissors, rps:cycle([dummy, dummy])), | |
?assertEqual(rock, rps:cycle([dummy, dummy, dummy])), | |
?assertEqual(paper, rps:cycle([dummy, dummy, dummy, dummy])), | |
?assertEqual(scissors, rps:cycle([dummy, dummy, dummy, dummy, dummy])). | |
least_frequent_test() -> | |
?assertEqual(scissors, rps:least_frequent([rock,paper,paper,scissors,scissors])), | |
?assertEqual(scissors, rps:least_frequent([paper,rock,paper,scissors,scissors])), | |
?assertEqual(scissors, rps:least_frequent([paper,paper,rock,scissors,scissors])), | |
?assertEqual(scissors, rps:least_frequent([scissors,paper,paper,rock,scissors])), | |
?assertEqual(scissors, rps:least_frequent([scissors,paper,paper,scissors,rock])). | |
most_frequent_test() -> | |
?assertEqual(paper, rps:most_frequent([rock,rock,paper,rock,scissors])), | |
?assertEqual(paper, rps:most_frequent([rock,paper,rock,scissors,rock])), | |
?assertEqual(paper, rps:most_frequent([paper,paper,rock,rock,rock])), | |
?assertEqual(paper, rps:most_frequent([rock,rock,rock,scissors,scissors])). | |
best_scored_strategy_for_a_draw_game_test() -> | |
% Setup | |
Strategies = #{rock => fun rps:rock/1, echo => fun rps:echo/1}, | |
OpponentMoves = [paper, paper, paper], | |
ExpectedBestScoredStrategyResult = paper, | |
% Assertions | |
?assertEqual( | |
ExpectedBestScoredStrategyResult, | |
(rps:best_scored(maps:to_list(Strategies)))(OpponentMoves) | |
). | |
best_scored_strategy_for_a_won_game_test() -> | |
% Setup | |
Strategies = #{rock => fun rps:rock/1, echo => fun rps:most_frequent/1}, | |
OpponentMoves = [paper, paper, paper], | |
ExpectedBestScoredStrategyResult = scissors, | |
% Assertions | |
?assertEqual( | |
ExpectedBestScoredStrategyResult, | |
(rps:best_scored(maps:to_list(Strategies)))(OpponentMoves) | |
). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(utils). | |
-author("[email protected]"). | |
-include_lib("eunit/include/eunit.hrl"). | |
-export([least_frequents/1, most_frequents/1,take/2]). | |
least_frequents(Xs) -> modes(Xs, fun minimum/1). | |
most_frequents(Xs) -> modes(Xs, fun maximum/1). | |
%% @doc Given a non empty list of elements, and a function that calculates | |
%% a maximum or minimum number, returns the least or more frequent ones. | |
modes(Xs, F) -> | |
Occurrences = lists:map(fun (X) -> occurrences(X, Xs) end, Xs), | |
MaxOccurrence = F(Occurrences), | |
ElementOccurrences = maps:from_list( | |
lists:zipwith(fun (X, Y) -> {X, Y} end, Xs, Occurrences) | |
), | |
maps:fold( | |
fun (Key, Value, Accumulator) -> | |
mode({Key, Value}, Accumulator, MaxOccurrence) | |
end, | |
[], | |
ElementOccurrences | |
). | |
%% @doc Given a tuple, an accumulator and a value chooses whether the tuple | |
%% value should be added or not. | |
mode({Key, Value}, Accumulator, Value) -> Accumulator ++ [Key]; | |
mode({_Key, _Value}, Accumulator, _MaxOccurrence) -> Accumulator. | |
modes_most_frequent_element_test() -> | |
Maximum = fun maximum/1, | |
?assertEqual([rock], modes([rock,paper,paper,rock,rock,scissors], Maximum)), | |
?assertEqual([rock], modes([rock,paper,paper,rock,rock,scissors], Maximum)), | |
?assertEqual([paper,rock], modes([rock,scissors,rock,rock,paper,paper,paper], Maximum)), | |
?assertEqual([paper], modes([paper,rock,rock,scissors,paper,paper,rock,paper], Maximum)), | |
?assertEqual([rock], modes([rock], Maximum)). | |
modes_least_frequent_element_test() -> | |
Minimum = fun minimum/1, | |
?assertEqual([scissors], modes([rock,paper,paper,rock,rock,scissors], Minimum)), | |
?assertEqual([scissors], modes([rock,paper,paper,rock,rock,scissors], Minimum)), | |
?assertEqual([paper,scissors], modes([rock,scissors,rock,rock,rock,rock,paper], Minimum)), | |
?assertEqual([scissors], modes([paper,rock,rock,scissors,paper,paper,rock,paper], Minimum)), | |
?assertEqual([rock], modes([rock], Minimum)). | |
%% @doc Given an element and a list returns the number of occurences of the | |
%% element in the given list. | |
occurrences(X, Xs) -> occurrences(X, Xs, 0). | |
%% @doc Given an element, a list of elements and an accumulator, deconstructs | |
%% a list while accumulating occurrences of the given element. | |
occurrences(_X, [], Occurrences) -> Occurrences; | |
occurrences(X, [X|Xs], Occurrences) -> occurrences(X, Xs, 1 + Occurrences); | |
occurrences(X, [_Y|Xs], Occurrences) -> occurrences(X, Xs, Occurrences). | |
occurrences_test() -> | |
?assertEqual(0, occurrences(rock, [paper,paper,paper,scissors,paper])), | |
?assertEqual(1, occurrences(rock, [paper,paper,paper,scissors,rock])), | |
?assertEqual(1, occurrences(rock, [paper,paper,rock,scissors,paper])), | |
?assertEqual(1, occurrences(rock, [rock,paper,paper,scissors,paper])), | |
?assertEqual(5, occurrences(rock, [rock,rock,rock,paper,rock,rock])). | |
%% @doc Given a non empty list of numbers returns the maximum using tail | |
%% recursion. | |
maximum([X|Xs]) -> maximum(Xs, X). | |
maximum([], Max) -> Max; | |
maximum([X|Xs], Max) -> maximum(Xs, max(Max, X)). | |
%% @doc Given a non empty list of numbers returns the minimum using tail | |
%% recursion. | |
minimum([X|Xs]) -> minimum(Xs, X). | |
minimum([], Max) -> Max; | |
minimum([X|Xs], Max) -> minimum(Xs, min(Max, X)). | |
%% @doc Given a number of elements and a list, returns a list with the first N | |
%% elements. | |
take(0,_Xs) -> | |
[]; | |
take(_N,[]) -> | |
[]; | |
take(N,[X|Xs]) when N>0 -> | |
[X|take(N-1,Xs)]. | |
take_test() -> | |
?assertEqual([], take(0, [1,2,3])), | |
?assertEqual([1], take(1, [1,2,3])), | |
?assertEqual([1,2], take(2, [1,2,3])), | |
?assertEqual([1,2,3], take(3, [1,2,3])), | |
?assertEqual([1,2,3], take(4, [1,2,3])). |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment