Erlang Thursday – c:xm/1

Today’s Erlang Thursday takes a turn down a slightly different route and takes a look in the c module at c:xm/1.

c:xm/1 takes either a atom, representing the name of a module, or a string, representing a filename, and inspects that module for unused and undefined functions, as well as calls to deprecated functions.

First let’s take a look at the erlang module, and see if there is anything in there that is deprecated.

c:xm(erlang).
% [{deprecated,[]},{undefined,[]},{unused,[]}]

Looks like there are no calls to deprecated functions, no calls to undefined functions, and no unused functions floating around in the erlang module. Note: This is running under Erlang 17.3.4, and you may get a different result depending on the version of Erlang you are running, because erlang:now/0 has been deprecated as of v18.0.

Trying to come up with an example of an existing module that might have some of these criteria, I took a look at the README for Erlang 17.0, and did a search for deprecated. Doing that there was a note:

The module pg has been deprecated and will be removed in Erlang/OTP 18.

So let’s pass that module to c:xm/1 and see what we get.

c:xm(pg).
% [{deprecated,[{{pg,create,1},{pg,master,1}},
%               {{pg,create,2},{pg,master,1}}]},
%  {undefined,[]},
%  {unused,[]}]

And we can see that we do get information back about deprecated functions in the pg module.

While the odds are low that you will need to use this function in your normal day to day work, as the tooling around Erlang generally seems to take care of this for you, this was intriguing enough that it seemed worthy of calling it out, especially if for those time when the compilation of Erlang code is done from inside the Erlang shell.

–Proctor

,

1 Comment

Ruby Tuesday – String#squeeze

Today’s Ruby Tuesday is on String#squeeze.

String#squeeze replaces runs of repeated characters in a source string down to a single character in the returned string.

"foo".squeeze
# => "fo"
"bar".squeeze
# => "bar"
"Mississippi".squeeze
# => "Misisipi"

String#squeeze can also take a string an argument. If a string is passed in, that string is seen as a set of characters to use for squeezing against the source string.

"Mississippi".squeeze("")
# => "Mississippi"
"Mississippi".squeeze("s")
# => "Misisippi"
"Mississippi".squeeze("abcprt")
# => "Mississipi"
"Mississippi".squeeze("sp")
# => "Misisipi"

If a string contains a - between two characters, that string is seen as containing a sequence of characters to include in the set of chacters to match for squeezing against the source string, but if it is first or last the - is seen as a character by itself.

"Mississippi".squeeze("a-r")
# => "Mississipi"
"Mississippi".squeeze("q-z")
# => "Misisippi"
"Mississippi".squeeze("pr-t")
# => "Misisipi"
"^^--~".squeeze('-~^')
# => "^-~"

If a string starts with a ^ then that string is seen as the negation of the set of characters that were given in that string to use, but if it is not the first character, then it is just seen as a normal ^ character.

"Mississippi".squeeze("^a-r")
# => "Misisippi"
"^^--~".squeeze('-^')
=> "^-~"

Backslashes can also be used to escape characters from any special meaning, but when using the backslash character in a double quoted string, then you have to make sure to escape that backslash with another backslash for it to be treated as an escape character in the string to String#squeeze.

"^^--~".squeeze('\^')
# => "^--~"
"^^--~".squeeze("\\^")
# => "^--~"
"^^--~~".squeeze('~\-^')
# => "^-~"

String#squeeze doesn’t modify the source string given, but returns a new instance of a string for the result. If you do need to modify the string in place for some reason, there exists a String#squeeze! as well, but like many other Ruby methods that end in a !, if the original item is not modified a nil is returned.

str = "Mississippi"
# => "Mississippi"
str.squeeze
# => "Misisipi"
str
# => "Mississippi"
str.squeeze!
# => "Misisipi"
str
# => "Misisipi"
"bar".squeeze!
# => nil
"foo".squeeze!
# => "fo"

–Proctor

,

Leave a Comment

Guest on Three Devs and a Maybe

I was recently asked to be on the podcast Three Devs and a Maybe, and am happy to announce that episode is now live.

I talked about my background of getting into computers, my route into functional programming, Clojure, Erlang, and a little bit off background on how I decided to start Functional Geekery.

Make sure to take a listen to Episode 71 – Erlang and Clojure with Steven Proctor and let me know what you think.

Thanks to Edd Mann and Michael Budd for the invite, and a great conversation.

–Proctor

,

Leave a Comment

Erlang Thursday – ordsets:is_subset/2

Today’s Erlang Thursday covers ordsets:is_subset/2.

ordsets:is_subset/2 takes two Ordered Sets, and checks if the ordered set passed in as the first argument is a subset of ordered set passed in as the argument. For a given set, Set A, to be a subset of another set, Set B, every item in Set A must also be a member of Set B.

SetA = ordsets:from_list(lists:seq(1, 10)).
% [1,2,3,4,5,6,7,8,9,10]
SetB = ordsets:from_list(lists:seq(2, 10, 2)).
% [2,4,6,8,10]
SetC = ordsets:from_list(lists:seq(1, 15, 3)).
% [1,4,7,10,13]
EmptySet = ordsets:new().
% []
ordsets:is_subset(SetB, SetA).
% true
ordsets:is_subset(SetA, SetB).
% false
ordsets:is_subset(SetC, SetA).
% false

And for those who aren’t as familiar with set theory, a few quick facts about sets. First, the empty set is a subset of all sets; second, a set is considered a sub-set of itself; and lastly, a given Set B is a superset of Set A, if Set A is subset of Set B.

ordsets:is_subset(EmptySet, SetA).
% true
ordsets:is_subset(EmptySet, SetB).
% true
ordsets:is_subset(EmptySet, SetC).
% true
ordsets:is_subset(EmptySet, EmptySet).
% true
ordsets:is_subset(SetA, SetA).
% true

–Proctor

,

Leave a Comment

Ruby Tuesday – Array#fetch

Today’s Ruby Tuesday covers a variation of one of my personal favorite set of Ruby methods, Array#fetch.

Array.fetch takes an integer for an argument and gets the element for a given index, using 0-based index scheme.

icao = [:alpha, :bravo, :charlie, :delta, :foxtrot]
# => [:alpha, :bravo, :charlie, :delta, :foxtrot]
icao.fetch 1
# => :bravo
icao.fetch 0
# => :alpha

It also can get you the elements walking backward from a list

icao.fetch -1
# => :foxtrot
icao.fetch -4
# => :bravo

If an index is out of bounds of the array, either positive of negative, an IndexError is raised.

icao.fetch 5
# IndexError: index 5 outside of array bounds: -5...5
# from (pry):5:in `fetch'
icao.fetch -7
# IndexError: index -7 outside of array bounds: -5...5
# from (pry):11:in `fetch'

At this point, I can hear you saying:

So what?! I can do this using a subscript to an array and not get exceptions but a nil when something is not found.
–You

icao[4]
# => :foxtrot
icao[0]
# => :alpha
icao[-1]
# => :foxtrot
icao[-7]
# => nil
icao[5]
# => nil

And you would be right.

But where you might run into trouble with just using a subscript index, is if you have nils in your array.

some_nils[2]
# => nil
some_nils[100]
# => nil

This is where Array#fetch makes its way to one of my favorite Ruby methods.

When you decide to use Array#fetch over just subscripts, you can specify a default value that you get back if the index is out of bounds.

icao.fetch 2, :fubar
# => :charlie
icao.fetch 5, :snafu
# => :snafu
icao.fetch -7, :fubar
# => :fubar

And to top it off, you can even pass a whole block which is called with the index passed to fetch, so you can do more detailed logic if needed.

icao.fetch(-7) { |index| "SNAFU occured trying to fetch with index #{index}" }
# => "SNAFU occured trying to fetch with index -7"

–Proctor

,

Leave a Comment

Erlang Thursday – ordsets:subtract/2

Today’s Erlang Thursday is on ordsets:subtract/2.

ordsets:subtract/2 takes two ordered sets as its arguments, and returns a ordered set containing the items of the first ordered set that are not in the second ordered set.

OrderedSetA = ordsets:from_list([5, 4, 3, 2, 1]).
# [1,2,3,4,5]
OrderedSetB = ordsets:from_list([1, 1, 2, 3, 5, 8, 13]).
# [1,2,3,5,8,13]
OrderedSetC = ordsets:from_list([2, -2, 4, -4, 16, -16]).
# [-16,-4,-2,2,4,16]
EmptySet = ordsets:new().
# []
ordsets:subtract(OrderedSetA, OrderedSetB).
# [4]
ordsets:subtract(OrderedSetA, EmptySet).
# [1,2,3,4,5]
ordsets:subtract(OrderedSetB, EmptySet).
# [1,2,3,5,8,13]
ordsets:subtract(EmptySet, OrderedSetA).
# []
ordsets:subtract(OrderedSetB, OrderedSetC).
# [1,3,5,8,13]

And note that ordsets:subtract/2 is not commutative, unlike ordsets:union/2 or ordsets:intersection/2.

ordsets:subtract(OrderedSetA, OrderedSetC).
# [1,3,5]
ordsets:subtract(OrderedSetC, OrderedSetA).
# [-16,-4,-2,16]

And again, your friendly reminder if you haven’t been following along, just because Ordered Sets in Erlang are represented as a List, doesn’t mean that Lists are Ordered Sets.

–Proctor

,

Leave a Comment

Ruby Tuesday – Array#sample

Today’s Ruby Tuesday is on Array#sample.

Array#sample has a couple of different forms it can take.

The first form it can take is a version where no arguments are passed; in this case it returns a random element from the array it was called on, or nil if the array is empty.

[1, 2, 3, 4, 5].sample
# => 4
[1, 2, 3, 4, 5].sample
# => 1
[1, 2, 3, 4, 5].sample
# => 2
[].sample
# => nil
[:heart, :diamond, :club, :spade].sample
# => :club

Array#sample can also take as its argument a count of the number of random items to be returned.

[1, 2, 3, 4, 5].sample 2
# => [1, 2]
[1, 2, 3, 4, 5].sample 2
# => [2, 1]
[1, 2, 3, 4, 5].sample 2
# => [1, 3]
[1, 2, 3, 4, 5].sample 2
# => [3, 5]
[1, 2, 3, 4, 5].sample 2
# => [5, 2]

If the argument to the count is greater than, or equal to, the number of items in the array, then the array is returned.

[:heart, :diamond, :club, :spade].sample 6
# => [:heart, :diamond, :spade, :club]

This has two subtle but important consequences. First if we call sample with a count on an empty array, an empty array is returned, instead of a nil like we would get back if we just call Array#sample with no count argument. This includes calling Array#sample with a count of zero on an empty array.

[].sample
# => nil
[].sample 0
# => []
[].sample 1
# => []
[].sample 3
# => []

Second, it shows that as we sample the array, we are not going to get duplicate elements, unless they were in the array to being with.

On both of these forms, a random number generator can also be passed. This can give us a repeatable generation sequence as we can prime the generator with a given seed.

generatorA = Random.new 1
# => #<Random:0x007f86c3925878>
(1..100).to_a.sample(random: generatorA)
# => 38
(1..100).to_a.sample(random: generatorA)
# => 13
(1..100).to_a.sample(random: generatorA)
# => 73
generatorB = Random.new 1
# => #<Random:0x007f86c497dd10>
(1..100).to_a.sample(random: generatorB)
# => 38
(1..100).to_a.sample(random: generatorB)
# => 13
(1..100).to_a.sample(random: generatorB)
# => 73
(1..100).to_a.sample(random: generatorB)
# => 10
(1..100).to_a.sample(random: generatorA)
# => 10

(1..100).to_a.sample(4, random: generatorA)
# => [76, 6, 82, 66]
(1..100).to_a.sample(4, random: generatorA)
# => [17, 2, 79, 74]
(1..100).to_a.sample(4, random: generatorB)
# => [76, 6, 82, 66]
(1..100).to_a.sample(4, random: generatorB)
# => [17, 2, 79, 74]

And if we want to pick a random card from a deck of cards, or even draw a 5 card hand we can use Array#sample on an array of “playing cards”.

suit = [:heart, :diamond, :club, :spade]
# => [:heart, :diamond, :club, :spade]
rank = [:ace, 2, 3, 4, 5, 6, 7, 8, 9, :jack, :queen, :king]
# => [:ace, 2, 3, 4, 5, 6, 7, 8, 9, :jack, :queen, :king]
suit.product(rank).sample 1
# => [[:heart, :jack]]
suit.product(rank).sample 5
# => [[:club, 2], [:diamond, 2], [:spade, 7], [:club, 8], [:spade, :king]]

–Proctor

,

Leave a Comment

Erlang Thursday Bonus! Performace of erlang:length/1 on a list

A bonus Erlang Thursday for everyone.

Giving the Dallas/Fort Worth Erlang User group presentation last week, we had a couple of people new to Erlang make it to our meeting, and the question was raised:

Do lists have any smarts around knowing their length, or does it have to run through all the items to calculate the length?

I was 99% sure that Erlang has to run through the list every time, since it uses linked lists style data structures for it’s list, but wasn’t sure if there might be something smart in the implementation that I wasn’t aware of to speed up that functionality.

In putting together the regularly scheduled Erlang Thursday post for today, I realized I should have busted out timer:tc to demonstrate the behavior of erlang:length/1 by showing how long it takes to get the length of different lists.

So in honor of that question, and as a reminder to review it at the next user group meeting, I am documenting the behavior here. And remember, that the first item in the result tuple is the time in microseconds the operation took.

timer:tc(erlang, length, [lists:seq(1, 10)]).
{2,10}
timer:tc(erlang, length, [lists:seq(1, 1000)]).
{5,1000}
timer:tc(erlang, length, [lists:seq(1, 10000)]).
{41,10000}
timer:tc(erlang, length, [lists:seq(1, 100000)]).
{134,100000}
timer:tc(erlang, length, [lists:seq(1, 1000000)]).
{1918,1000000}
timer:tc(erlang, length, [lists:seq(1, 10000000)]).
{25139,10000000}
timer:tc(erlang, length, [lists:seq(1, 100000000)]).
{1368691,100000000}

After about 1000 items in a linked list, we start growing lineraly in the time it takes to count the items, so if it is not doing an actual traversal of all the items, it has the same scale of doing so as far as the order of operations (Big-O) goes.

–Proctor

,

Leave a Comment

Erlang Thursday – ordsets:intersection/2

Today’s Erlang Thursday looks some more at that ordsets module and covers ordsets:intersection/2.

ordsets:intersection/2 takes two ordered sets and returns a new ordered set that is the intersection of the two ordered sets provided. For those who don’t have a background wth set theory, all a set intersection is is the set of items that all the sets we are intersecting have in common.

OrderedSet1 = ordsets:from_list([1, 2, 1, 3, 2, 4, 4, 9]).
[1,2,3,4,9]
OrderedSet2 = ordsets:from_list([1, 3, 5, 7, 9]).
[1,3,5,7,9]
ordsets:intersection(OrderedSet1, OrderedSet2).
[1,3,9]
ordsets:intersection(OrderedSet2, OrderedSet1).
[1,3,9]

Because ordsets:intersection/2 looks for the common elements in the ordered sets, it is commutative, and as we see above, we get the same result regardless of which order we pass in the two ordered sets as arguments.

If there are no items in common, the returned result is an empty ordered set (really an empty list, but see last week’s post on ordsets:union/2 on the dangers of just using a list as a ordered set).

Evens = ordsets:from_list(lists:seq(2, 20, 2)).
[2,4,6,8,10,12,14,16,18,20]
Odds = ordsets:from_list(lists:seq(1, 20, 2)).
[1,3,5,7,9,11,13,15,17,19]
ordsets:intersection(OrderedSet2, ordsets:new()).
[]
ordsets:intersection(Evens, Odds).
[]

Erlang also provides ordsets:intersection/1, that takes a list of ordered sets as its argument, and returns the intersection of all the ordered sets in that list.

OrderedSet3 = ordsets:from_list([1, 1, 2, 3, 5, 8]).
[1,2,3,5,8]
ordsets:intersection([Evens, Odds, OrderedSet1]).
[]
ordsets:intersection([Odds, OrderedSet2, OrderedSet1]).
[1,3,9]
ordsets:intersection([Evens, OrderedSet1, OrderedSet3]).
[2]
ordsets:intersection([Odds, OrderedSet1, OrderedSet3]).
[1,3]

–Proctor

,

Leave a Comment

Ruby Tuesday – Array#compact

Today’s Ruby Tuesday in on Array#compact.

Array#compact operates against a given array and returns a new array with any nils removed.

array1 = [1, 2, nil, 3, nil, nil, 4, nil]
# => [1, 2, nil, 3, nil, nil, 4, nil]
array2 = array1.compact
# => [1, 2, 3, 4]
array1
# => [1, 2, nil, 3, nil, nil, 4, nil]
array2
# => [1, 2, 3, 4]
Array.new(7)
# => [nil, nil, nil, nil, nil, nil, nil]
Array.new(7).compact
# => []
[].compact
# => []
array_with_no_nils = [1, 2, 3, 4, 5]
# => [1, 2, 3, 4, 5]
compacted_array_with_no_nils = array_with_no_nils.compact
# => [1, 2, 3, 4, 5]
compacted_array_with_no_nils
# => [1, 2, 3, 4, 5]
array_with_no_nils
# => [1, 2, 3, 4, 5]

As seen in the examples above, Array#compact preserves the original order of the elements in the source array.

Array#compact only performs a shallow removal of any nils, and does not remove nils from any nested arrays.

mixed_type_array = [nil, :a, nil, nil, "b", [1, 2, nil, 3], nil, :c]
# => [nil, :a, nil, nil, "b", [1, 2, nil, 3], nil, :c]
mixed_type_array.compact
# => [:a, "b", [1, 2, nil, 3], :c]

And because nils in Ruby are falsey, we can do a quick sanity check about if Array#compact is looking at nils specifically, or falsey items in general, by creating a new Array filled with falses and calling compact on it.

Array.new(9){false}.compact
=> [false, false, false, false, false, false, false, false, false]

The Array class also defines Array#compact!, which modifies the underlying state of the Array it is being called on.

array3 = array1.dup
# => [1, 2, nil, 3, nil, nil, 4, nil]
array4 = array3.compact!
# => [1, 2, 3, 4]
array3
# => [1, 2, 3, 4]
array4
# => [1, 2, 3, 4]

As we can see in the above example, if the array compact! is being called on does have nils in it, the return value is the modified state of the Array.

But if you call Array#compact! on an array that does not have any nils in it, the return value of Array#compact! is nil.

Array.new(9){false}.compact!
# => nil

–Proctor

,

Leave a Comment