Not_a_Number has asked for the wisdom of the Perl Monks concerning the following question:

To simplify: let's say I have an array of numbers, some positive, some negative, some zero, that I want to print out like so:

  +1  -4  +3   0  -2   0 -14

That is to say, with '+' signs before each positive number. If I use the following code:

my @nums = qw ( 1 -4 3 0 -2 0 -14 ); printf '%+4d', $_ for @nums;

I get plus signs in front of my zeros, which I don't want:

  +1  -4  +3  +0  -2  +0 -14

In the docs for (s)printf - which incidentally I find very sparse - it says that the '+' flag is used to "prefix (a) positive number with a plus sign". I'm not a mathematician, so I'm prepared to accept that zero is a positive number. But I still don't wan't plus signs in front of my zeros :-(

I've come up with various solutions, and for the moment, my code looks like this:

printf $_ == 0 ? ( '%4d', $_ ) : ( '%+4d', $_ ) for @nums;

...but the more I stare at it, the more it looks like line noise. OK, I could expand and comment it; however, my question is, is there a better way?

TIA

dave

Replies are listed 'Best First'.
Re: (s)printf question: eliminating plus sign before zeros
by Zaxo (Archbishop) on Dec 17, 2003 at 21:46 UTC

    There is no printf format which does that, so far as I know. Your solution is about what you need to do. It can be simplified a bit, printf +( $_ ? '%+4d' : '%4d' ), $_ for @nums;

    After Compline,
    Zaxo

      Zaxo++

      In fact, playing with your solution, I can 'simplify' it further

      printf $_ ? '%+4d' : '%4d', $_ for @nums;

      But that's starting to look like golf...

      dave

        Did someone mention golf:)

        printf $_ ? '%+4d' : ' 0', $_ for -4 .. +4; -4 -3 -2 -1 0 +1 +2 +3 +4

        Actually it doesn't shorten the code any, but it maybe saves a few microseconds in parsing, both by the computer and the human being. Maybe?


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "Think for yourself!" - Abigail
        Hooray!

Re: (s)printf question: eliminating plus sign before zeros
by tachyon (Chancellor) on Dec 18, 2003 at 01:56 UTC

    If it REALLY bugs you then you could always just patch the source (totally untested):

    [root@devel3 perl-5.8.2]# diff uts/sprintf_wrap.c uts/sprintf_wrap.PAT +CH.c 114c114 < else if(Plus) *ob++ = '+'; --- > else if(Plus && ! Zero && ! AllZeroes ) *ob++ = '+'; 148c148 < if(Plus && (Ecvt[0] != '-')) { --- > if(Plus && (Ecvt[0] != '-') && ! Zero ) { [root@devel3 perl-5.8.2]#

    cheers

    tachyon

      Umm, that source is just used for UTS (whatever OS that is) presumably because of deficiencies in its native sprintf. A patch would need to be to Perl_sv_vcatpvfn in sv.c.

        It was just a joke!

        cheers

        tachyon

Re: (s)printf question: eliminating plus sign before zeros
by ysth (Canon) on Dec 18, 2003 at 00:05 UTC
    Disclaimer: this is a joke.

    You can get rid of that pesky + by changing: printf '%+4d', $_ for @nums; to printf '%+4.0f', $_||-.1 for @nums; Update: yes, that will produce "-0" which is correct behaviour for negative numbers that printf %f has to round to 0.

      Thanks. That really helps. Now I've got minus zero instead of plus zero.

      ;-)

      dave

Re: (s)printf question: eliminating plus sign before zeros
by delirium (Chaplain) on Dec 18, 2003 at 12:47 UTC
    Handy map to the rescue ...albeit somewhat late:

    $,=' '; my @nums = qw ( 1 -4 3 0 -2 0 -14 ); print map {sprintf '%+d', $_ if $_} @nums
Re: (s)printf question: eliminating plus sign before zeros
by duff (Parson) on Dec 17, 2003 at 22:05 UTC

    Here's something odd. I was trying to "improve" on Zaxo's solution and I discovered this:

    printf "%*d", +4, $_ for @nums;

    On my system at least, it works as Not_a_Number requested.

    Update: D'oh! I conflated the output of two of my runs.

      Doesn't work for me (I get no plus signs before positive numbers) on WinXP AS 5.61.

      Output:

         1  -4   3   0  -2   0 -14

      dave