Tag Archives: Erlang

Erlang Thursday – calendar:day_of_the_week/3

Today’s Erlang Thursday is covers calendar:day_of_the_week/3.

calendar:day_of_the_week/3 allows you to get the day of the week a day occurred on when passed a year, a month and a day.

The first argument represents the year and must be a non-negative integer. The second argument is the month and must be an integer value between 1 and 12 inclusive, representing the 12 months of the Gregorian Calendar, with January being month one. The final argument to calendar:day_of_the_week/3 is the date, and is expected to be a integer between 1 and 31 inclusive.

calendar:day_of_the_week/3 returns an integer value in between 1 and 7 inclusive, with a return value of 1 representing Monday, and a 7 being Sunday.

calendar:day_of_the_week(1, 1, 1).
% 1
calendar:day_of_the_week(1970, 1, 1).
% 4
calendar:day_of_the_week(1999, 12, 31).
% 5
calendar:day_of_the_week(0, 1, 1).
% 6

As you can see that the publish date of this Erlang Thursday post, 2015-04-09, returns a 4, which is in fact a Thursday according to Erlang.

calendar:day_of_the_week(2015, 4, 9).
% 4

There is also a calendar:day_of_the_week/1 function which has all the same constraints but instead of taking three arguments, takes one argument that is a three-tuple of the year, month, and day.

calendar:day_of_the_week({2015, 4, 9}).
% 4
calendar:day_of_the_week({1970, 1, 1}).
% 4
calendar:day_of_the_week({1999, 12, 31}).
% 5

And in the spirit of helping to recognize error messages when we see them, let’s take a look at what we get when we pass some invalid inputs to calendar:day_of_the_week/3.

calendar:day_of_the_week(0, 0, 0).
% ** exception error: no function clause matching calendar:date_to_gregorian_days(0,0,0) (calendar.erl, line 114)
%      in function  calendar:day_of_the_week/3 (calendar.erl, line 151)
calendar:day_of_the_week(1970, 2, 31).
% ** exception error: no true branch found when evaluating an if expression
%      in function  calendar:date_to_gregorian_days/3 (calendar.erl, line 116)
%      in call from calendar:day_of_the_week/3 (calendar.erl, line 151)
calendar:day_of_the_week(1970, 13, 2).
% ** exception error: no function clause matching calendar:last_day_of_the_month1(1970,13) (calendar.erl, line 243)
%      in function  calendar:date_to_gregorian_days/3 (calendar.erl, line 115)
%      in call from calendar:day_of_the_week/3 (calendar.erl, line 151)

If you look at the error messages you see that calendar:day_of_the_week/3 calls calendar:date_to_gregorian_days/3, which we will cover in next week’s Erlang Thursday.

–Proctor

Erlang Thurday – lists:delete/2

Today’s Erlang Thursday in lists:delete/2.

lists:delete/2 takes a Erlang term as it’s first argument, and will remove that item from the list passed in as the second argument.

lists:delete(1, [8, 7, 6, 5, 4, 3, 2, 1]).
% [8,7,6,5,4,3,2]
lists:delete(4, [1, 1, 2, 3, 5, 8]).
% [1,1,2,3,5,8]
lists:delete(72, "Hello World!").
% "ello World!"
lists:delete(d, [a, b, c, d]).
% [a,b,c]
lists:delete(4, []).
% []
lists:delete({b, 2}, [{a, 1}, {b, 2}, {c, 3}]).
% [{a,1},{c,3}]
lists:delete([1, 2, 3], [[4, 5, 6], [7, 8, 9], [1, 2, 3]]).
% [[4,5,6],[7,8,9]]

Note that lists:delete/2 only removes the first item found in the list, and leaves any other occurrences of the item in the list.

€lists:delete(1, [1, 1, 2, 3, 5, 8]).
% [1,2,3,5,8]

As lists:delete/2 was a easy function to demonstrate, and leaving it at this would be a very short post, I thought it might be worth showing a how you might write a very naive1 implementation of lists:delete/2 yourself.

-module(my_lists).

-export([delete/2]).

delete(Item, List) ->
    delete(Item, [], List).

delete(Item, Checked, [Item|Rest]) ->
    lists:reverse(Checked) ++ Rest;
delete(Item, Checked, [Head|Rest]) ->
    delete(Item, [Head | Checked], Rest);
delete(_Item, Checked, []) ->
    lists:reverse(Checked).

Let’s start with our delete function as we expect it to be called from the outside world.

my_lists:delete/2 is the nice API function that just calls delete/3 which is a “private” function (not exported) so the consumer doesn’t have to worry about passing in the accumulator for the items we have checked so far, which we pass as an empty list for the initial value.

-module(my_lists).

-export([delete/2]).

delete(Item, List) ->
    delete(Item, [], List).

The first function clause of delete/3 uses pattern matching to check if the item we want to delete is also the first item in the rest of the list to check. If the pattern match succeeds, we have found the first occurrence of the item to remove! We can stop processing the list and return the result, which is the reverse of the list of items we have checked so far combined with the rest of the items we never got around to checking.

delete(Item, Checked, [Item|Rest]) ->
    lists:reverse(Checked) ++ Rest;

The second clause “knows” that the item we are wanting to delete and the first item in the rest of the list don’t match. How does it “know”? Because if they did match, the first clause would have matched and this clause would not have been evaluated. As we haven’t found the item to remove, we add the item held by Head to the list of Checked items, and then continue calling delete/3. The fact that we are passing a new list of the checked items by prepending Head to the list in Checked is why we need to reverse Checked in the first and third function clauses.

delete(Item, Checked, [Head|Rest]) ->
    delete(Item, [Head | Checked], Rest);

The third, and final, clause of delete/3 has reached the end of the list and not found the item, so we just return the list we have reversed it.

delete(_Item, Checked, []) ->
    lists:reverse(Checked).

There you have it, a very naive1 implementation of your very own version of lists:delete/2.

–Proctor

1. Naive because this is not optimized for performance, or exhaustively tested for completely accurate behavior of lists:delete/2. back

Erlang Thursday – erlang:list_to_atom/1

Today’s Erlang Thursday covers erlang:list_to_atom/1.

erlang:list_to_atom/1 takes a string, and returns an Erlang atom.

list_to_atom("foo").
% foo
list_to_atom("Foo").
% 'Foo'
list_to_atom("Foo_bar").
% 'Foo_bar'
list_to_atom("foo_bar").
% foo_bar
list_to_atom("foo"++"bar").
% foobar
list_to_atom("Erlang").
% 'Erlang'
list_to_atom("Elixir").
% 'Elixir'

This can be useful if you are having to create keys or identifiers based off strings read in from outside the system, such as parsing a CSV style header.

lists:map(fun erlang:list_to_atom/1,
          string:tokens("firstName,lastName,age,gender,preferredName,dateOfBirth", ",")).
% [firstName,lastName,age,gender,preferredName,dateOfBirth]

You do need to be careful when using erlang:list_to_atom/1 on strings acquired from the outside world of your program, as it only handles strings with character values under 256. But any character value1 under 256 is fair game to be turned into an atom.

list_to_atom("Joe, Mike, and Robert").
% 'Joe, Mike, and Robert'
list_to_atom("it's").
% 'it's'
list_to_atom("heyn").
% 'heyn'€
list_to_atom("with_supported_char"++[255]).
% with_supported_charÿ
list_to_atom("with_non_supported_char"++[256]).
% ** exception error: bad argument
%      in function  list_to_atom/1
%         called as list_to_atom([119,105,116,104,95,110,111,110,95,115,117,112,112,111,114,
%                                 116,101,100,95,99,104,97,114,256])

–Proctor


1. Remember that character values are non-negative integer values as well. 0-255 inclusive. back

Erlang Thurday – ordsets:is_disjoint/2

Today’s Erlang Thurday covers ordsets:is_disjoint/2.

There are times in your in your coding day where you have problems where you need to know if given a list of items that none of those items appear in a secondary list.

Your first intuition might be to write out the code as described, like such:

not( lists:any(fun(Item) -> lists:member(Item, [1, 3, 5, 7]) end, [2, 4, 6])).
% true

And while that is accurate, if you redefine your problem in more mathematical terms, you can start to think in sets. When you start to think in terms of sets, you realize that you can check to see if the intersection of the two sets is the empty set.

ordsets:intersection([1, 3, 5, 7], [2, 4, 6]) =:= [].
% true

This is becoming not only more concise, but also more explicit about what you are trying to check.

We can do better still, by checking if the lists are disjoint sets. Enter ordsets:is_disjoint/2.

ordsets:is_disjoint/2 takes two lists, and returns true if no elements are in common.

ordsets:is_disjoint([1, 3, 5, 7], [2, 4, 6]).
% true
ordsets:is_disjoint([1, 2, 3, 5, 7], [2, 4, 6]).
% false

Because ordsets:is_disjoint/2 operates against two lists, we do not have to make sure the elements are unique prior to calling ordsets:disjoint/2.

ordsets:is_disjoint([1, 1, 3, 5, 7, 5, 3], [2, 4, 2, 2, 6]).
% true
€ordsets:is_disjoint([1, 2, 3, 5, 7], [2, 4, 2, 2, 6]).
% false

And if either list passed to ordsets:is_disjoint/2 is an empty list, the result is that the lists are disjoint.

ordsets:is_disjoint([1, 2, 3, 5, 7], []).
% true
ordsets:is_disjoint([], [2, 4, 6]).
% true
ordsets:is_disjoint([], []).
% true

And if you are curious, by running ordsets:is_disjoint/2 through timer:tc/3, we can see that as soon as Erlang knows that the sets are not disjoint, it returns false. And if you remember from the previous Erlang Thrusday on timer:tc/3, the return value is a tuple with the first element being the number of microseconds it took to complete.

timer:tc(ordsets, is_disjoint, [lists:seq(1, 1000000), lists:seq(2000000, 3000000)]).
% {19032,true}
€timer:tc(ordsets, is_disjoint, [lists:seq(1, 1000000), lists:seq(1, 3000000)]).
% {2,false}

–Proctor

Erlang Thursday – lists:flatmap/2

Today’s Erlang Thursday is on lists:flatmap/2.

The selection of this function comes from some Elixir exercises I was doing earlier in the week, and was getting confused because when using the Enum.flat_map/2 function in Elixir wasn’t working as I expected it to.

I circled back to Erlang to check the behavior of lists:flatmap/2 in Erlang, and after getting over my confusion that the flatmap function wasn’t working in Erlang, the common behavior made sense, and I realized I contorted the concept in my head and it was time to take a step back and figure out what lists:flatmap/2 actually does.

Somehow what I had convinced myself that lists:flatmap/2 did was to take a list of items that were nested arbitrarily deep, and mapped over the flattened list in the equivalent of this:

lists:map(fun(X) -> X * X end, lists:flatten([1, [[2, [3]], 4]])).
% [1,4,9,16]

Looking closer at the documentation, after even trying Ruby’s flat_map function, it was clear I had completely confused myself as to how it should work, so time to carefully read the Erlang docs.

They stated that lists:flatmap/2 takes a function that takes an item of type A and returns a list of items that are of type B, and that the second argument to lists:flatmap/2 was a list of items of type A.

Being already confused based off how I thought it should work, it wasn’t registering until I saw the part of the documentation that describes that lists:flatmap/2 behaves as if defined as

flatmap(Fun, List1) ->
    append(map(Fun, List1)).

This started to click for me for what lists:flatmap/2 was actually doing. In the picture that I had in my head, I was expecting to flatten the list first, and then map over the items, but it does the map first, and then does only the flatten, but of only one level deep.

lists:flatmap(fun({Item, Count}) -> 
                  lists:duplicate(Count, Item)
              end,
              [{a, 1}, {b, 2}, {'C', 3}, {'_d_', 4}]).
% [a,b,b,'C','C','C','_d_','_d_','_d_','_d_']

And if we pass those values to the “equivalent” behavior of calling map and then calling append on the list returned from map, we see the results are the same.

lists:append(
    lists:map(fun({Item, Count}) ->
                  lists:duplicate(Count, Item)
              end,
              [{a, 1}, {b, 2}, {'C', 3}, {'_d_', 4}])).
% [a,b,b,'C','C','C','_d_','_d_','_d_','_d_']

And to further clarify, lists:flatmap/2 doesn’t even do a flatten on the resulting list, but simply adjoins the lists that were returned from the mapping function. This can be seen below, as we can see there is still an nested list structure in the results, and the resulting list is not only one level deep.

lists:flatmap(fun(X) -> [X, [X]] end, [a, b, c, d]).
% [a,[a],b,[b],c,[c][/c],d,[d]]

Hope this can save you the confusion I managed to get myself into,
–Proctor

Erlang Thursday – calendar:local_time_to_universal_time_dst/1

In honor of the time change this weekend, today’s Erlang Thursday is on calendar:local_time_to_universal_time_dst/1.

To better understand what calendar:local_time_to_universal_time_dst/1 is doing, it will be contrasted to calendar:local_time_to_universal_time/1.

This coming Sunday is we move the clock forward an hour at 2 AM, so let us see what the time is in UTC right at 1:59 AM.

calendar:local_time_to_universal_time({{2015,3,8},{1,59,59}}).
% {{2015,3,8},{7,59,59}}

Now let’s see what calendar:local_time_to_universal_time/1 returns for 2 AM, which we never actually hit, since the time changes straight to 3 AM.

calendar:local_time_to_universal_time({{2015,3,8},{2,00,00}}).
% {{2015,3,8},{8,0,0}}

But as we dig into the library we see that there is also calendar:local_time_to_universal_time_dst/1. calendar:local_time_to_universal_time_dst/1 returns a list of time tuples, and we start to see why this is important.

This time, let’s call calendar:local_time_to_universal_time_dst/1 with the 2 AM hour and see what is returned.

calendar:local_time_to_universal_time_dst({{2015,3,8},{2,00,00}}).
% []

An empty list!

This makes sense if you take a moment to think about it, as 2 AM doesn’t ever happen so there is no UTC time that it would map to.

And if we look at what happens when we make the jump to 3 AM, we get 8 AM UTC, which is just a second later in UTC, then the time in UTC at 1:59:59 AM.

calendar:local_time_to_universal_time_dst({{2015,3,8},{3,00,00}}).
% [{{2015,3,8},{8,0,0}}]

That covers the case for this coming weekend, so let is check out what is going to happen later this year on November 1st when the clocks move back an hour.

To start with a baseline, let’s see what time it is in UTC at 12:59:59 AM, as we know we only encounter that time once.

calendar:local_time_to_universal_time({{2015,11,1},{0,59,59}}).
% {{2015,11,1},{5,59,59}}
calendar:local_time_to_universal_time_dst({{2015,11,1},{0,59,59}}).
% [{{2015,11,1},{5,59,59}}]

And even though the time change happens a 2 AM, we still only encounter that once, as we move straight to 1 AM the first time.

calendar:local_time_to_universal_time_dst({{2015,11,1},{2,0,0}}).
% [{{2015,11,1},{8,0,0}}]

So at 12:59:59 AM, we are at 5:59:59 AM UTC, and at 2 AM we are at 8 AM UTC. Now comes the tricky part, 1 AM.

1 AM is an odd case, as we will live through 1 AM twice that night. So let’s see what Erlang does with that.

First, we see what time it is in UTC when we call calendar:local_time_to_universal_time with 1 AM.

calendar:local_time_to_universal_time({{2015,11,1},{1,00,00}}).
% {{2015,11,1},{6,0,0}}

We get 6 AM UTC. We move from 5:59:59 AM UTC to 6 AM UTC; and that makes sense, until we start to wonder about the second time we encounter 1 AM.

Now, let’s see what time it is in UTC when we call calendar:local_time_to_universal_time_dst/1 with 1 AM.

calendar:local_time_to_universal_time_dst({{2015,11,1},{1,0,0}}).
% [{{2015,11,1},{6,0,0}},{{2015,11,1},{7,0,0}}]

We get a list with two times in UTC, one for 6 AM UTC when we encounter 1 AM the first time, and one for 7 AM UTC for when we encounter 1 AM the second time around. This happens for every time in the 1 AM hour as well.

calendar:local_time_to_universal_time_dst({{2015,11,1},{1,59,59}}).
% [{{2015,11,1},{6,59,59}},{{2015,11,1},{7,59,59}}]

By returning a list of either no items, one item, or two items, Erlang gives you the most accurate information for you to use, and allow for you to make the informed decision for how you are expecting to handle the times, instead of making some of the decisions for you even if they don’t align with your system’s view of the world.

–Proctor

Erlang Thursday – dict:merge/3

Today’s Erlang Thursday is on dict:merge/3.

dict:merge/3 takes 3 arguments, the first argument is a merge function to be called when there is a key collision, and the second and third arguments are dictionaries.

The merge function is a function that takes the key as the first argument, the value from the first dictionary as the second argument, and the value from the second dictionary as the the third argument.

dict:merge(fun (_Key, Value1, Value2) -> [Value1, Value2] end,
           dict:from_list([{a, 1}, {b, 2}, {x, 5}]),
           dict:from_list([{x, 7}, {y, 8}, {z, 10}])).
% {dict,5,16,16,8,80,48,
%       {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
%       {{[],
%         [[a|1]],
%         [[b|2]],
%         [],[],[],[],[],
%         [[x,5,7]],
%         [[y|8]],
%         [[z|10]],
%         [],[],[],[],[]}}}

dict:merge(fun (_Key, Value1, Value2) -> Value1 * Value2 end,
           dict:from_list([{a, 1}, {b, 2}, {x, 5}]),
           dict:from_list([{x, 7}, {y, 8}, {z, 10}])).
% {dict,5,16,16,8,80,48,
%       {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
%       {{[],
%         [[a|1]],
%         [[b|2]],
%         [],[],[],[],[],
%         [[x|35]],
%         [[y|8]],
%         [[z|10]],
%         [],[],[],[],[]}}}

The merge function passed to dict:merge/3 only gets called in the case of a collision, as shown below. Note that there is a call to exit in the body of the function which would cause the process to terminate if the function was ever invoked.

dict:merge(fun (_Key, _Value1, _Value2) -> exit(merge_happened) end,
           dict:from_list([{a, 1}, {b, 2}]),
           dict:from_list([{x, 7}, {y, 8}, {z, 10}])).
% {dict,5,16,16,8,80,48,
%       {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
%       {{[],
%         [[a|1]],
%         [[b|2]],
%         [],[],[],[],[],
%         [[x|7]],
%         [[y|8]],
%         [[z|10]],
%         [],[],[],[],[]}}}

If you wish to treat the merge as an overlay of the second dictionary over the first, the merge function just needs to return the value from the second dictionary in the case of a key conflict.

dict:merge(fun (_Key, _Value1, Value2) -> Value2 end,
           dict:from_list([{a, 1}, {b, 2}, {x, 5}]),
           dict:from_list([{x, 7}, {y, 8}, {z, 10}])).
% {dict,5,16,16,8,80,48,
%       {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
%       {{[],
%         [[a|1]],
%         [[b|2]],
%         [],[],[],[],[],
%         [[x|7]],
%         [[y|8]],
%         [[z|10]],
%         [],[],[],[],[]}}}

If you want to keep all of the keys and values in the first dictionary, and just add the keys and values that are in the second dictionary, but not in the first dictionary, the merge function should just return the value associated with the first dictionary.

dict:merge(fun (_Key, Value1, _Value2) -> Value1 end,
           dict:from_list([{a, 1}, {b, 2}, {x, 5}]),
           dict:from_list([{x, 7}, {y, 8}, {z, 10}])).
% {dict,5,16,16,8,80,48,
%       {[],[],[],[],[],[],[],[],[],[],[],[],[],[],[],[]},
%       {{[],
%         [[a|1]],
%         [[b|2]],
%         [],[],[],[],[],
%         [[x|5]],
%         [[y|8]],
%         [[z|10]],
%         [],[],[],[],[]}}}

Just a peek into the new Maps that came in to Erlang in the 17.0 release.

–Proctor

Erlang Thursday – string:join/2

Today’s Erlang Thursday is on string:join/2.

string:join/2 takes a list of strings as its first argument, and a string separator used to join the strings together into a single string.

91> string:join(["a", "b", "c"], "").
"abc"
92> string:join(["a", "b", "c"], "-").
"a-b-c"

The separator string can be a string of any length, and doesn’t just have to be a single character.

93> string:join(["a", "b", "c"], "___").
"a___b___c"
94> string:join(["a", "b", "c"], " ").  
"a b c"

And as with any string, a list of characters, or even integers, can be used as the separator string.

string:join(["a", "b", "c"], [$A]).
# "aAbAc"
string:join(["a", "b", "c"], [52]).
# "a4b4c"

–Proctor

Erlang Thursday – string:tokens/2

Today’s Erlang Thursday is string:tokens/2.

string:tokens/2 takes a string as the first argument, and a list of separators to split the string on, and returns a list of token strings.

string:tokens("foo", "").
% ["foo"]
string:tokens("banana", "a").
% ["b","n","n"]
string:tokens("It was the best of times, it was the worst of times", " ").
% ["It","was","the","best","of","times,","it","was","the",
%  "worst","of","times"]

If consecutive separators appear in the string they will be treated as a single separator, and no empty strings will be returned.

string:tokens("Mississippi", "s").
% ["Mi","i","ippi"]
65> string:tokens("Mississippi", "sp").
% ["Mi","i","i","i"]
string:tokens("Mississippi", "is").
% ["M","pp"]

The order of the separators in the separator list passed to string:tokens/2 does not matter, and can be specified in any order.

string:tokens("Mississippi", "ps").
% ["Mi","i","i","i"]
65> string:tokens("Mississippi", "sp").
% ["Mi","i","i","i"]

And as the separator list is just simply a list of separators, instead of passing a string, the integer values for the characters to use as the separators can be passed as a list, as a list of the integers is the same as a string.

$s.
% 115
$p.
% 112
[115, 112].
% "sp"
string:tokens("Mississippi", [115]).
% ["Mi","i","ippi"]
string:tokens("Mississippi", [115, 112]).
% ["Mi","i","i","i"]

–Proctor

Erlang Thursday – lists:dropwhile/2

Today’s Erlang Thursday is lists:dropwhile/2.

lists:dropwhile/2 takes a predicate function and a list, and returns a list where the first series of items for which the predicate function returned true have been removed.

lists:dropwhile(fun erlang:is_atom/1, [hello, 'World', foo, 1, 3, 4]).
% [1,3,4]
lists:dropwhile(fun (X) -> X > 0 end, [-1, 0, 1, 2, 3]).
% [-1,0,1,2,3]
lists:dropwhile(fun (X) -> X > 0 end, [-2, -1, 0, 1, 2, 3]).
% [-2,-1,0,1,2,3]
lists:dropwhile(fun (X) -> X < 0 end, [-2, -1, 0, 1, 2, 3]).
% [0,1,2,3]
lists:dropwhile(fun (X) -> X < 0 end, [0, -1, -2, -3, -4, -5]). 
% [0,-1,-2,-3,-4,-5]
lists:dropwhile(fun (X) -> true end, [hello, 'World', foo, 1, 3, bar, 4]). 
% []
lists:dropwhile(fun (X) -> false end, [hello, 'World', foo, 1, 3, bar, 4]).
% [hello,'World',foo,1,3,bar,4]

Unlike lists:filter/2, lists:dropwhile/2 stops checking the list as soon as the predicate function returns false. This means that elements for which the predicate function would return true can still appear in the result list, as if they occur after an element for which the predicate function returns false.

lists:dropwhile(fun erlang:is_atom/1, [hello, 'World', foo, 1, 3, bar, 4]).
% [1,3,bar,4]
lists:filter(fun (X) -> not is_atom(X) end, [hello, 'World', foo, 1, 3, bar, 4]).     
% [1,3,4]
lists:dropwhile(fun (X) -> X < 0 end, [-2, -1, 0, 1, -5, 3, 7]).
% [0,1,-5,3,7]
lists:filter(fun (X) -> X >= 0 end, [-2, -1, 0, 1, -5, 3, 7]).   
% [0,1,3,7]

–Proctor