Skip to content

Instantly share code, notes, and snippets.

@granolocks
Last active December 18, 2015 03:19
Show Gist options
  • Select an option

  • Save granolocks/5717375 to your computer and use it in GitHub Desktop.

Select an option

Save granolocks/5717375 to your computer and use it in GitHub Desktop.
Most of the functions in this module are inspired by the guides in this site: http://learnyousomeerlang.com/
-module(recursive_functions).
-export([
factorial/1,
tail_fac/1,
tail_fac/2,
recursive_length/1,
tail_len/1,
tail_len/2,
duplicate/2,
tail_duplicate/2,
tail_duplicate/3,
reverse/1,
tail_reverse/1,
tail_reverse/2
]).
-author("Gabe Koss").
%% oh look! Recursion to calculate factorials as in:
%%
%% 4! = 4 x 3!
%% 4! = 4 x 3 x 2!
%% 4! = 4 x 3 x 2 x 1!
%% 4! = 4 x 3 x 2 x 1 x 1
%%
%% Pattern match on 0 to return 1
factorial(0) -> 1;
%% otherwise recursively calculate down til N = 0
factorial(N) when N > 0 -> N*factorial(N-1).
%% This is a bit of a memory hog, so alternatively we can use what is called "tail recursion"
%% This is similar to a ruby inject
%%
%% tail_fac(4) = tail_fac(4,1)
%% tail_fac(4,1) = tail_fac(4-1, 4*1)
%% tail_fac(3,4) = tail_fac(3-1, 3*4)
%% tail_fac(2,12) = tail_fac(2-1, 2*12)
%% tail_fac(1,24) = tail_fac(1-1, 1*24)
%% tail_fac(0,24) = 24
%%
%% Note: this requires two exported functions since the arity is not the same
tail_fac(N) -> tail_fac(N,1).
tail_fac(0,Acc) -> Acc;
tail_fac(N,Acc) when N > 0 -> tail_fac(N-1, N*Acc).
%% Here is another recursive function to calculate the length of a list...
%% O_o this is mind boggling
%% from http://learnyousomeerlang.com/recursion
%% len([1,2,3,4]) = len([1 | [2,3,4])
%% = 1 + len([2 | [3,4]])
%% = 1 + 1 + len([3 | [4]])
%% = 1 + 1 + 1 + len([4 | []])
%% = 1 + 1 + 1 + 1 + len([])
%% = 1 + 1 + 1 + 1 + 0
%% = 1 + 1 + 1 + 1
%% = 1 + 1 + 2
%% = 1 + 3
%% = 4
%% establish base case
recursive_length([]) -> 0;
%% recursivel work towards base case
recursive_length([_|T]) -> 1 + recursive_length(T).
%%
%% and to use Tail recursiion
tail_len(N) -> tail_len(N,0).
tail_len([],Acc) -> Acc;
tail_len([_|T], Acc) -> tail_len(T, Acc+1).
%% Another one, this takes a Number and a Term and then returns a list with
%% a number of elements equal to the seeded number, entirely consisting of the Term
duplicate(0,_) ->
[];
duplicate(N,Term) when N > 0 ->
[Term|duplicate(N-1,Term)].
%% and using the tail recursion logic
%%
%% tail_duplicate(4,foo) = tail_duplicate(4, foo, [])
%% = tail_duplicate(3, foo, [foo])
%% = tail_duplicate(2, foo, [foo, foo])
%% = tail_duplicate(1, foo, [foo, foo, foo])
%% = tail_duplicate(0, foo, [foo, foo, foo, foo])
%% = [foo, foo, foo, foo]
%%
tail_duplicate(N,Term) ->
tail_duplicate(N,Term,[]).
%% This flavor has an arity of 3, allowing it to include a
%% collector list
%%
%% When N is reduced to 0, disregard Term
%% and return the List
tail_duplicate(0,_,List) ->
List;
tail_duplicate(N,Term,List) ->
%% decrement N, add Term as new head of List,
tail_duplicate(N-1,Term,[Term|List]).
%% recursively reverse a list
%% reverse([1,2,3,4]) = [4]++[3]++[2]++[1]
%% ↑ ↵
%% = [4,3]++[2]++[1]
%% ↑ ↑ ↵
%% = [4,3,2]++[1]
%% ↑ ↑ ↑ ↵
%% = [4,3,2,1]
reverse([]) -> [];
reverse([H|T]) -> reverse(T) ++ [H].
%% with tail recursion
%%
%% tail_reverse([1,2,3]) = tail_reverse([1,2,3],[])
%% = tail_reverse([2,3], [1])
%% = tail_reverse([3], [2,1])
%% = tail_reverse([], [3,2,1])
%% = [3,2,1]
tail_reverse(List) ->
tail_reverse(List,[]).
tail_reverse([], Acc) ->
Acc;
tail_reverse([H|T], Acc) ->
tail_reverse(T, [H|Acc]).
%% Most of the functions in this module are inspired by the guides in
%% this site: http://learnyousomeerlang.com/
%%
%% Hyphen prefixed declarations are headers used at compile time.
%%
%% Module Declaration
-module(sample_module).
%%
%% List of exportable functions
%% FORMAT: function_name/<arity>
%%
-export([
calculate_age/1,
head/1,
second/1,
same_value/2,
valid_time/1,
say_hello/2,
say_age/1,
say_age_with_if/1,
say_age_with_case_of/1,
case_age_string/1,
greet_and_calculate_age/3,
factorial/1,
recursive_length/1
]).
-author("Gabe Koss").
%%
%% Define a static value or a macro.
%% obviously the Current Year may not be the best thing to set ;)
%% this is just for the syntax example and a study reminder
%%
-define(CURRENT_YEAR,2013).
%% notice how the pattern matching
%% is used to extract info from the list
head([Head|_]) ->
Head.
second([_,Second|_]) ->
Second.
%% another example of this for pattern matching
%% as a way to defining the function behavior
same_value(X,X) ->
true;
same_value(_,_) ->
false.
%% Another example of this... god this is cool
%% It is easy to see how this will become useful for
%% json validation and other types of parsing
%%
%% In the first example we take 1 tuple as an argument
%% that contains two named tuples of three Named values
%%
%% Note: This has an arity of 1
valid_time({
Date = {Y,M,D},
Time = {H,Min,S}
}) ->
io:format("The Date tuple (~p) says today is: ~p/~p/~p,~n",[Date,Y,M,D]),
io:format("The Time tuple (~p) indicates: ~p:~p:~p.~n", [Time,H,Min,S]);
%% In the second version, anythin gthat doesn't match the expected format
%% raises an error
valid_time(_) ->
io:format("Stop feeding me wrong data!~n").
%% basic print hello world function
%%
%% NOTE: how the behavior of the function is defined by
%% by the arguments definition
%%
%% Ruby equivalent:
%%
%% def hello(gender, name)
%% if gender == :male
%% puts "Hello, Mr. #{name}!\n"
%% elsif gender == :female
%% puts "Hello, Mr. #{name}!\n"
%% else
%% puts "Hello, #{name}!\n"
%% end
%% end
say_hello(male, Name) ->
io:format("Hello, Mr. ~s!~n",[Name]);
say_hello(female, Name) ->
io:format("Hello, Ms. ~s!~n",[Name]);
say_hello(_, Name) ->
io:format("Hello, ~s!~n",[Name]).
%% Subtract the year two values
calculate_age(YearOfBirth) ->
?CURRENT_YEAR - YearOfBirth.
%% The when clause after the function declaration is called a "Guard"
%% which serves to validate the format of arguments
say_age(Age) when Age > 0, Age =< 130 ->
io:format("You are, ~s!~n",[Age]);
say_age(Age) when Age =< 0 ->
io:format("You are... a negative number of years old... Are you Merlin?!~n");
say_age(_) ->
io:format("You are far too old!~n").
%% An alternative approach for this would be a more traditional
%% if statement. This is less than ideal for Erlang, but
%% is being added here for the purpose of of an example.
%%
%% See the what_the_if module at http://learnyousomeerlang.com/syntax-in-functions for more info about
%% why this is a bad idea
say_age_with_if(Age) ->
String = if Age =< 0 -> "You are... a negative number of years old... Are you Merlin?!";
Age >= 130 -> "You are far too old!";
%% This interpolation style makes me sad, MUST be a better way to do this
%% from: ttp://stackoverflow.com/questions/588003/convert-an-integer-to-a-string-in-erlang
true -> lists:flatten(io_lib:format("You are ~p!", [Age]))
end,
io:format(String ++ "~n").
%% One final variant on this same theme would be a case ... of statement
case_age_string(Age) ->
case Age of
N when N > 0, N =< 130 ->
%% for fun an tlernate interpolation trick
"You are " ++ erlang:integer_to_list(N) ++ "!";
N when N =< 0 ->
"You are... a negative number of years old... Are you Merlin?!";
_ -> "You are far too old!"
end.
say_age_with_case_of(Age) ->
io:format(case_age_string(Age) ++ "~n").
greet_and_calculate_age(Gender, Name, YearOfBirth) ->
say_hello(Gender, Name),
say_age(calculate_age(YearOfBirth)).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment