Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number

Is what you want available?

by Lady_Aleena (Curate)
on Jun 08, 2017 at 22:07 UTC ( #1192403=perlmeditation: print w/replies, xml ) Need Help??

As some of you may know, I am trying to become a better Perl writer. Part of it is finding where I wrote something that can be done better by someone else and using the better way.

One time I needed to do a numeric comparison of months written as text. So I wrote a module to get the month numbers needed. After I wrote it, I found to my dismay, I had not needed to write it. Date::Calc had a subroutine called Decode_Month that was much better than what I wrote. I am so embarrassed I did not think to look in Date::Calc first before I wrote my silly little module. Date::Calc is far better at date manipulation than anything I could write. So, I dropped the use of my module for Date::Calc. It was a learning experience I took forward to the rest of my code.

Another part of becoming better is rewriting and renaming the modules I have written. To find names for my modules, I have been crawling CPAN to find what name spaces already exist.

I was looking for a new name for my Util::Number module. I crawled CPAN and found "Number" is a top name space. Then I found Number::Format. I sighed thinking my little Util::Number's days were numbered. Number::Format has a subroutine called round and so did Util::Number. Both do the same thing. Number::Format has a subroutine called format_number that puts commas in numbers and rounds numbers together, just like commify and my pretty_number. I thought commify and pretty_number were not going to be able to match format_number's power.

So, I installed Number::Format and was going to discontinue using my Util::Number. Why? Using Number::Format in code that I need help with, you can install it from CPAN without any trouble. Using Util::Number in code that I need help with, you would have to guess at what it does unless I also put Util::Number in the node with my problem code.

So, I was changing over my code from pretty_number to format_number and making sure it returned what I wanted. Then I got a big surprise. format_number has limitations commify and pretty_number do not have. format_number can not put commas into very big numbers. It outputs an error about the big number and suggests using Math::BigFloat.


perl -e 'use Number::Format qw(format_number); print format_number(123 +45678987654321)."\n";'


round() overflow. Try smaller precision or use Math::BigFloat at -e li +ne 1.


perl -e 'use Util::Number qw(commify); print commify(12345678987654321 +)."\n";'



In the change over from commify and pretty_number to format_number, I got another surprise. format_number does not do ordinal numbers either. I fed it an ordianal number in Happy unbirthday redux! and other birthday stuff, and it returned the following error.

Argument "16725th" isn't numeric in numeric comparison (<=>) at /home/ +me/perl5/lib/perl5/Number/ line 599.

So, I went back to commify in my code and got...

Happy 16,725th unbirthday, Aleena! You have 32 days until your next bi +rthday on July 10, 2017.

This was all so surprising to me. Here I was ready to use a module readily available on CPAN instead of my own silly little module, but the CPAN module could not do what mine does. I was thinking "But it's is so much better than mine. It's got all those bells and whistles. And it's on CPAN!"

This is not a rant against Number::Format. It is on CPAN and is better written than my little Util::Number. I will use Number::Format in code when I need help. Number::Format also has options I do not currently have, so I will be using it whenever one of those options is needed. Util::Number may become a fallback module when Number::Format will not work for what I am doing currently. Util::Number will not be going to CPAN since I did not write commify or round, I just put them together in pretty_number.


package Util::Number; use strict; use warnings FATAL => qw( all ); use Exporter qw(import); our @EXPORT_OK = qw(commify round pretty_number); # commify was found in the perlfaq5 to put commas in numbers. sub commify { local $_ = shift; 1 while s/^([-+]?\d+)(\d{3})/$1,$2/; return $_; } # sprintf written by james2vegas on PerlMonks. sub round { my ($number, $precision) = @_; my $rounded = $number =~ /\./ ? sprintf("%.${precision}f", $number) +: $number; return $rounded; } sub pretty_number { my ($number, $precision) = @_; my $pretty_number = commify(round($number, $precision)); return $pretty_number; } =head1 NAME B<Util::Number> adds commas, rounds, and returns pretty numbers. =head1 SYNOPSIS use Util::Number qw(commify round pretty_number); my $comma_number = commify(2468); # returns 2,468 my $rounded_number = round(0.2468, 3); # returns .247 my $pretty_number = pretty_number(2468.13579, 3); # returns 2,468.136 =head1 DESCRIPTION B<Util::Number> contains three subroutines that make numbers prettier: + C<commify>, C<round>, and C<pretty_number>. =head2 commify B<C<commify>> returns a number with commas between every three digits +in the number. The code was found in L<perlfaq5| +#How-can-I-output-my-numbers-with-commas-added?>. =head2 round B<C<round>> rounds a decimal number by a set precision. If you want th +e number returned with three digits after the decimal, the precision +would be set to 3. round($number, $precision); =head2 pretty_number B<C<pretty_number>> puts commify and round together so you can get rou +nded numbers with commas. pretty_number($number, $precision); =head1 AUTHOR Lady Aleena with help =cut 1;

I am trying to make my modules more useful with better names and code. I am also trying to get better by using what is already available. However, sometimes, what is available is a little less than what is wanted. That does not mean I will stop searching.


I cracked opened my now disused month module just to look at it. Then I saw the hash of arrays. What do I do with hashes of arrays of similar data? I randomize it of course. So my Util::MonthNumber is now Random::Month... 8)


package Random::Month; use strict; use warnings FATAL => qw( all ); use Exporter qw(import); our @EXPORT_OK = qw(random_month); use Fancy::Rand qw(fancy_rand); my %months = ( 'English' => [qw(January February March April May June July Aug +ust Spetember October November December)], 'English abbr' => [qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Now De +c)], 'Dutch' => [qw(januari februari maart april mei juni juli aug +ustus september oktober november december)], 'French' => [qw(janvier février mars avril mai juin juillet ao +űt septembre octobre novembre décembre)], 'German' => [qw(Januar Februar März April Mai Juni Juli August + September Oktober November Dezember)], 'Greek' => [qw(Ianuários Fevruários Mártios Aprílios Máios Iú +nios Iúlios Avghustos Septémvrios Októvrios Noémvrios Thekémvrios)], 'Italian' => [qw(gennaio febbraio marzo aprile maggio giugno lu +glio agosto settembre ottobre novembre dicembre)], 'Spanish' => [qw(enero febrero marzo abril mayo junio julio ago +sto septiembre octubre noviembre diciembre)], ); # I wrote this hash for another reason, but Date::Calc killed the need + for the original purpose. sub random_month { my ($user_language, $user_additions) = @_; my $random_month = fancy_rand(\%months, $user_language, { caller => +'random_month', additions => $user_additions ? $user_additions : unde +f }); return $random_month; } =head1 NAME B<Random::Month> selects a random month by language. =head1 SYNOPSIS use Random::Month qw(random_month); my $random_month = random_month(); my $random_English_month = random_month('English'); my $random_Dutch_month = random_month('Dutch'); my $random_French_month = random_month('French'); my $random_German_month = random_month('German'); my $random_Greek_month = random_month('Greek'); my $random_Italian_month = random_month('Italian'); my $random_Spanish_month = rnadom_month('Spanish'); my $random_English_month_abbr = random_month('English abbr'); print random_month('help') # get random_month options =head1 AUTHOR Lady Aleena =cut 1;
No matter how hysterical I get, my problems are not time sensitive. So, relax, have a cookie, and a very nice day!
Lady Aleena

Replies are listed 'Best First'.
Re: Is what you want available?
by stevieb (Canon) on Jun 09, 2017 at 13:14 UTC

    What has helped me figure out better names for my distributions, better code in my distributions and better scoping the functionality of my distributions from very early on was (and still is) to open up other's projects and write patches for them. Most have been accepted, others not. The ones that weren't, the authors were kind enough to explain why. For example, years and years ago, I wanted an addition to Type::Tiny. My patch wasn't accepted because the author thought my change took away from the "Tiny" aspect. Reading other's work and writing patches is a great way to learn.

    For other things, such as adding a feature that doesn't reflect what the module does directly what it's name implies (such as the missing ordinal functionality in Number::Format) is to sublcass the module, and add it yourself (eg: Number::Format::Ordinal). This gives you practical experience in understanding someone else's API, as well as incorporating that into your own work. It also forces you to read others' documentation and that helps you write your own better (or, supply doc bug fixes if you find errors/omissions between what their docs say and what the code says. Patching documentation is a valuable lesson as well, as it will undoubtedly cause you to improve your own POD writing skills.

    Part of becoming a confident distribution writer for the CPAN is re-inventing the wheel for practice, part of it is learning how others do things, another part is learning to use other good modules if they are available, and gauging whether an existing project can incorporate an idea you have reasonably sanely.

    To this day, and I've been doing this for 15 years now, is look at the most recent distributions/dist updates on the CPAN a few times per week, open up ones that I either use, find interesting or think I may use in the future into new browser tabs, then I read through the docs and the code and check for obvious issues. If there's nothing worth opening a ticket for or worthy of patching, I think about how I might use the distribution (or have used it in the past) and consider if there are any deficiencies or additions that could be made. This again teaches new ways to do things (or finding out that the distribution you always thought was great was simply a ticking time bomb in which case you learn how NOT to do things ;)

    Anyway, rambling on. I'm half way through a 12 hour drive through the mountains to northern BC, waiting for my girlfriend in a hotel room to finish getting ready so we can take off again.

Re: Is what you want available?
by Arunbear (Prior) on Jun 09, 2017 at 11:55 UTC
    Commifying has been done more thoroughly. This handles long numbers correctly but doesn't work with input like "1234th". But it's designed to work with numbers so no surprise it won't handle a string as input. OTOH I would not expect a module called Util::Number to handle string data such as "1234th".

    Rounding has also been done more thoroughly i.e. Math::Round

    "pretty_number" is quite a vague name that doesn't give any clues about what it actually does.

      I think we a bit of a terminology problem. Ordinal numbers are numbers. So all of these current modules are a bit misnamed themselves. It appears they only handle integers and decimals. It is a shame they all left off ordinal numbers. Also, if you think pretty_number is hard to figure, how would most people know what CLDR::Number is? I had to run to Google to find out what CLDR meant. That was the first time I had ever seen that abbreviation.

      Math::Round looks a bit overcomplicated.

      Thanks for stopping by!

      No matter how hysterical I get, my problems are not time sensitive. So, relax, have a cookie, and a very nice day!
      Lady Aleena
        I think we a bit of a terminology problem. Ordinal numbers are numbers.

        They are, but not in the sense that you mean.

        So all of these current modules are a bit misnamed themselves. It appears they only handle integers and decimals.

        None of them expect to handle alphabetical string input (or AFAICT, complex input). That's not a misnaming, at least not on their part. Consider a chandlery called "Smith's Boatyard" which does a fine business outfitting RIBs and so on. Would you say they were misnamed because they do not cater for aircraft carriers?

        Plenty of advice has been given elsewhere in this thread for how you might handle such quirks. Don't berate a solid, working, published module just because you believe that it should cater for something for which it was never designed.

        Disclaimer: I am not the author or maintainer of any of the mentioned modules - it just riles me to see their efforts maligned for no good reason.

Re: Is what you want available?
by shmem (Chancellor) on Jun 09, 2017 at 18:24 UTC
    However, sometimes, what is available is a little less than what is wanted.

    Whenever you encounter a module which provides a little less, you have 3 ways to add the missing bits (using Number::Format and Util::Number in the following):

    1. copy the source of Number::Format to Util/, add the missing subroutine and use your renamed private copy (brittle)
    2. write Util::Number as a subclass which uses Number::Format and extends its functionality with your own subs (better)
    3. switch packages in your code and add the enhanced subroutines there (hack)

    The first decouples you from the development of Number::Format. You'd have to update your private module if you want to benefit from the improvements of Number::Format as it developes.

    The second possibility places on you the tiny burden of writing tests for your module, but only for those bits you have added.

    The third may be used if and only if a) you use Number::Format only in one place, b) the added bits are trivial, c) you know where to look when things break.

    So, I'd go with number 2.

    package Util::Number; use parent Number::Format; sub pretty_number { ... } 1;
    perl -le'print map{pack c,($-++?1:13)+ord}split//,ESEL'

      Don't forget 4) Offer the module owner a patch to add the functionality.

Re: Is what you want available?
by Arunbear (Prior) on Jun 09, 2017 at 12:22 UTC
    "random_month" could be made more user friendly by supporting keyword arguments e.g.
    my $random_month = random_month(-lang=> 'English', -abbrev=> 1);
    You may also consider supporting ISO 639-1 codes for the language specifier
    my $random_month = random_month(-lang=> 'en', -abbrev=> 1); my $random_German_month = random_month(-lang=> 'de');
    Also, there are good reasons for not using fatal warnings.

      How is adding even more typing user-friendly, especially when there is only 1 variable? Also, since there is only 1 language I have abbreviations for, it seems a bit silly to make a user type in a second variable for it, when I would have to do something like the following in the subroutine to make it read.

      my $language = $opt{'lang'}.' '.$opt{'abbr'};

      Wouldn't it be less of a headache to just have an all in one without splitting the hash key name between two variables?

      my $month = random_month('English abbr'); -or- my $month = random_month( 'lang' => 'English', 'abbr' => 1 );

      I also expanded the selection of languages and how the languages can be selected.

      About fatal warnings, I leave them in for me. I consider all warnings as bad as errors, because something is not right with the code if I'm being warned about it. If I were to ever make any module "official", I would it them out.

      No matter how hysterical I get, my problems are not time sensitive. So, relax, have a cookie, and a very nice day!
      Lady Aleena
        User friendliness includes having sensible defaults. English is a sensible default language. So if I wanted a random month in the default language I'd expect to be able to do
        my $month = random_month();
        Now if I wanted an abbreviated random month in the default language I'd expect to be able to do
        my $month = random_month(-abbrev => 1);
        Language and abbreviation are two distinct concepts, which indicates you need two parameters to handle them cleanly,
Re: Is what you want available?
by Anonymous Monk on Jun 10, 2017 at 06:44 UTC
    I am trying to become a better Perl writer. Part if it is finding where I wrote something that can be done better by someone else and using the better way.

    To become a better Perl "writer", stop reinventing the wheel.

    If I have a task, my first step is to search CPAN to see if what I want to do has already been done. If so, I evaluate the matches and see if anything fits and seems decent. If so, use it, if not, it's probably a hole in the coverage and it's time to start coding a new wheel.

    I don't mean to be harsh; I have recoded a lot of wheels just for the learning experience but it's better to stand on the shoulders of giants.

Re: Is what you want available?
by sundialsvc4 (Abbot) on Jun 09, 2017 at 12:33 UTC

    The CPAN library contains, at this writing, “186,946 modules.”   Unsurprisingly, a great many of them are redundant.   Anyone can contribute one, and I am quite sure that many of them arrive in the library more-or-less as yours might:   “I wrote this, I thought it was cool, and I wanted to share it.”   Thank you.

    But you want your contribution, of course, to actually be used.   (At one time it was estimated that over 60% of the apps in Apple’s on-line store had never been downloaded ... at all.)   You want your contribution to be memorable.   To do that, first it must be found.   I literally have to stumble upon your module to know that it exists, and to know that it is right for me.   It will attract my second-glance if it does one or two things “thoroughly,” and if these things that it does are not entirely trivial.   But it all starts with the module name, and the words that you use to describe it.   Util::Number is as non-descriptive as it gets.   Phrases like “pretty” also convey no sense of “what you mean by that.”   Say exactly what your module does, and, in the synopsis, give a short but descriptive example.   If there is a particular word or phrase that I might distinctly be searching-for in order to separate the wheat from the chaff in my search, use that word or phrase.

    I will take at least a passing glance at the source-code.   Make it neat and tidy.   Make sure that I can see it all, on the CPAN site, without downloading it.

    Your module does not have to be a Swiss Army® knife, full of both source-code and dependencies, to be successful.   There is plenty of room for a good pocketknife or even a good bottle-opener.   It simply won’t be the only, nor the first, module that does more-or-less the same thing. But it must be thoroughly implemented, non-trivial (“It is to my advantage to download something to do this, rather than do it myself”), very well-tested (that is to say, good pre-installation online test suite), and well-named and described.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://1192403]
Approved by marinersk
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others about the Monastery: (5)
As of 2020-09-29 12:54 GMT
Find Nodes?
    Voting Booth?
    If at first I don’t succeed, I …

    Results (146 votes). Check out past polls.