LFE Friday - ordsets:is_disjoint/2
This week's LFE Friday was translated with permission from the Erlang Thursday series by Steven Proctor. This week's translator: Robert Virding.
Today's LFE Friday 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 (lambda (item) (lists:member item '(1 3 5 7))) '(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 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
ordsets:is_disjoint/2 operates against two lists, we do not have to make sure the elements are unique prior to calling
> (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
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 LFE Friday 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 (list (lists:seq 1 1000000) (lists:seq 2000000 3000000))) #(13627 true) > (timer:tc 'ordsets 'is_disjoint (list (lists:seq 1 1000000) (lists:seq 1 3000000))) #(1 false)