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

Dear Monks

While thinking everything should be working fine, I got the following problem, demonstrated by the code below:
#! /usr/bin/perl -lw use strict ; my %r = ( 'a' => 1, 'b' => 2, 'c' => 3) ; my $keys = keys(%r) ; print "keys = $keys" ; printf "keys=%d\n", keys(%r) ;
Output
keys = 3 Argument "c" isn't numeric in printf at ./t12.pl line 12. keys=0
So, the second time keys(%r) is not used in scalar context. I tried everything I could think of to get it into scalar context, but only
printf "keys=%d\n", (my $sc = keys(%r)) ;
seems to work, which doesn't look like the best solution :)
Any suggestions ?

Thnx
LuCa

Replies are listed 'Best First'.
Re: keys(%hash) not in scalar context in printf
by Joost (Canon) on Apr 18, 2007 at 11:59 UTC
Re: keys(%hash) not in scalar context in printf
by Herkum (Parson) on Apr 18, 2007 at 12:05 UTC

    When you are doing this

    my $keys = keys(%r) ;

    which is basically this,

    my $keys = @keys;

    When you are doing the scalar assignment perl can figure out what value you are looking for and assign it to the scalar. Since the default behavior is normally to get the size of the array that is why you get the number of elements in the array.

    Your problem is that printf does not implicitly identify keys(%r) to be an array conversion, in large part because an array is a acceptable parameter for printf. Take this example.

    printf "Number 1: %d\nNumber 2:%d\nNumber 3:%d\n", values(%r);

    If you want force the scalar you should use the scalar function to get what you want.

    printf "No of keys=%d\n", scalar keys(%r);
Re: keys(%hash) not in scalar context in printf
by roboticus (Chancellor) on Apr 18, 2007 at 12:03 UTC
    jeanluca:

    Try this...

    #! /usr/bin/perl -lw use strict ; my %r = ( 'a' => 1, 'b' => 2, 'c' => 3) ; my $keys = keys(%r) ; print "keys = $keys" ; printf "keys=%d\n", scalar(keys(%r)) ;
    ...roboticus
Re: keys(%hash) not in scalar context in printf
by cdarke (Prior) on Apr 18, 2007 at 12:13 UTC
    printf takes an item list after the format string, so everything is in list context. To force into scalar context you can use scalar or a scalar operator, such as . (dot). The reason your assignment works is because the left side is a scalar, which also forces the right to be scalar context.
    You don't really need printf here:
    print 'keys: '.keys(%hash)."\n";
    uses the . operator to force into scalar.
Re: keys(%hash) not in scalar context in printf
by jeanluca (Deacon) on Apr 18, 2007 at 12:31 UTC
    I always get into trouble when things are in list context. Hopefully thats over now!!

    thanks a lot monks!!
    LuCa
Re: keys(%hash) not in scalar context in printf
by liverpole (Monsignor) on Apr 18, 2007 at 13:19 UTC
    Hi jeanluca,

    My own preference in this case is almost always:

    printf "keys=%d\n", 0+(keys %r);

    I just like the brevity of that over scalar.


    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
      Hello, all.

      With respect to the above code, it should be known that this will be interpreted in the exact same fashion as the scalar call, possibly with added instructions.

      The use as an rvalue in a mathematical expression to the right of a scalar (0) forces the interpretation of the list in scalar context, as the lexer parses from left to right.

      While you're saving a keystroke or two, you are making absolutely no gains in terms of performance, and potentially losing some if your platform is actually foolish enough to perform the addition to zero.

      Furthermore, as a powerful perl monk, one must remember to *occasionally* pay heed to the twin divinities of readability and maintainability, lest they smite thee and thy code with hours of frustrating bug-hunts.

      The "scalar" solution is by far the most straight-forward and accepted way to handle this.

      Obidan

        The use as an rvalue in a mathematical expression to the right of a scalar (0) forces the interpretation of the list in scalar context, as the lexer parses from left to right.

        Note that your "to the right of" can be dropped and that everything after your comma above is irrelevent. keys(%hash) + 0 would work just as well for getting at the number of keys even in a list context.

        While you're saving a keystroke or two, you are making absolutely no gains in terms of performance, and potentially losing some if your platform is actually foolish enough to perform the addition to zero.

        I almost never use scalar. I frequently use 0+ and sometimes use ''. exactly because they are clearer than scalar. 0+ means that I want to interpret the following thing as a number. Perhaps this is so natural to me since I was using it way back in the day with Turbo Pascal. I use ''. when I want to make it clear that I'm getting at the value as a string. In Perl6, just + will be enough to say "I want the number".

        I don't care about saving a few keystrokes nor about performance trivialities. I care about readability and maintainability. 0+ says more than scalar. Sure, pick any part of Perl and you'll likely find some that aren't familiar with it. But even someone who isn't familiar with 0+ can likely figure out that the result is a number. I find that 0+keys... is more likely to be understood as returning "the number of keys" even by someone not deeply familiar with Perl. So I find it much clearer than scalar keys... (more natural to understand while conveying more information).

        - tye