I'm currently doing database programming, and wanted to view a list of users and their passwords, along with certain permissions being granted for a few of them.  Since the names and passwords are fairly long, and there's more than 1 permission, I wanted to print out not just the permission value (0 or 1), but a string of asterisks or spaces, so I could quickly spot which users have which permissions assigned.

So I wrote some "throwaway code" (where "TFD" stands for "Temporary for debugging"):

printf "TFD> %32.32s %16.16s %s %s\n", $user, $pass, ($b_admin? '*': ' ') x 8, ($b_reports? '*': ' ') x 8;
and was surprised to find that only a single asterisk was printed out for those users which had the permissions set.

I had expected that the ternary operator '?:' should have produced either an asterisk '*' or a space ' ', and then that character would have been expanded to a string of 8 characters.

After thinking about it for a minute and not coming up with any answers, it seemed sensible to write a simplified test program to investigate.  My first pass was this:

#!/usr/bin/perl -w # Strict use strict; use warnings; # Libs use Data::Dumper; # User-defined my $pusers = { jack => 1, jill => 0, tom => 1, dick => 0, harry => 0, thelma => 0, louise => 1, }; # Main program my $mark; foreach my $user (sort keys %$pusers) { my $flag = $pusers->{$user}; printf "%10.10s %s\n", $user, ($flag? '*': ' ') x 8; }
As expected, the "problem" was present, as the output reveals a single asterisk where I thought I should see 8 of them:
dick harry jack * jill louise * thelma tom *
Next I tried doing what I was sure would work, beside what was failing, and changed the main program to:
# Main program my $mark; foreach my $user (sort keys %$pusers) { my $flag = $pusers->{$user}; printf "%10.10s %s\n", $user, ($flag? '*': ' ') x 8; $mark = $flag? '*': ' '; printf "%10.10s %s\n", $user, $mark x 8; }
Sure enough, the termary operator was "working" when the variable $mark was assigned ahead of time, but "failing" when used all on one line:
dick dick harry harry jack * jack ******** jill jill louise * louise ******** thelma thelma tom * tom ********
Realizing that I wasn't sure what arguments were being passed to printf, I wrote a wrapper subroutine myprintf(), to intercept and display those arguments:
# Subroutines sub myprintf { my (@args) = @_; printf "TFD> %s\n", Dumper(\@args); } # Main program my $mark; foreach my $user (sort keys %$pusers) { my $flag = $pusers->{$user}; $mark = $flag? '*': ' '; myprintf "%10.10s %s\n", $user, $mark x 8; myprintf "%10.10s %s\n", $user, ($flag? '*': ' ') x 8; }
Jackpot!  It's now fairly clear from the output what's happening:
TFD> $VAR1 = [ '%10.10s %s ', 'jack', '********' ]; TFD> $VAR1 = [ '%10.10s %s ', 'jack', '*', '*', '*', '*', '*', '*', '*', '*' ];
In the former case, the scalar variable $mark along with the repetition operator 'x' was producing an 8-character string.  In the latter case, however, the 'x' operator was being applied to a list, and thus producing a list of 8 separate asterisks '*'.

I was now able to fix the original problem by forcing scalar context on my list, with something like:

printf "TFD> %32.32s %16.16s %s %s\n", $user, $pass, scalar ($b_admin? '*': ' ') x 8, scalar ($b_reports? '*': ' ') x 8;

Or even more simply:

printf "TFD> %32.32s %16.16s %s %s\n", $user, $pass, ''.($b_admin? '*': ' ') x 8, ''.($b_reports? '*': ' ') x 8;

The point of all this is that Perl is complex, and that complexity can make it a difficult language to learn.  Understanding and writing code using the various contexts in Perl, like many other Perl constructs (eg. the ternary operator '?:' and the repetition operator 'x'), is by no means an easy task.  But it's well worth the effort because, in the end, you have the advantage of a richer, more powerful language with which to do your work.

And the next time I need to construct a complicated repetition within a string, I'll think twice about which context I'm in.


@ARGV=split//,"/:L"; map{print substr crypt($_,ord pop),2,3}qw"PerlyouC READPIPE provides"

Replies are listed 'Best First'.
Re: On Solving a Simple Problem, and Appreciating the Complexity of Perl
by zentara (Cardinal) on Mar 03, 2006 at 16:09 UTC
    Perl is complex, and that complexity can make it a difficult language to learn. ............ But it's well worth the effort because, in the end, you have the advantage of a richer, more powerful language

    It reminds me of English. The big problem foreign students have in learning it, is that many words have multiple meanings, which we determine dynamically by the context in which they are used. Someone born and raised speaking english, takes it as second nature, but coming from a different culture... its a mind blower.


    I'm not really a human, but I play one on earth. flash japh
      As you say, in English "many words have multiple meanings, which we determine dynamically by the context in which they
      are used". That sounds like "operator overloading" to me...
      The problem with English is that it's a kitchen-sink language; part Latin, part French, part Anglo-Saxon, with parts borrowed from the rest of the world, too.

      Context is important; and subtle variations in word choice can change the percieved context of a sentence: consider "meat", "flesh", "body", "corpse", or "carcass".

      All refer to the something that was once alive and is now dead, but the word choice conveys a different "mood" and frames a different way of the looking at a given scenario.

      English is "rich" in the sense that there a subtle shades of meaning; and like Perl, few people are literate enough to understand most of them. It makes English hard to debug; when a non-native speaker of English makes a mistake, it's hard to know which mistake he or she made, when there are many possible choices that could be meant.

      I've heard "Jane" pronounced as "chain", "chen", "Jen", "Jan", "Shayne", or "Jay". Like Perl, there are too many valid parses for a mistake; and like Perl, this makes figuring out what someone was trying to say far too much work at times.

      --
      Ytrew,
      "who hates having to decipher the streetcar driver's accent just to recognize the name of his own street :-("

        Update: Corrected link!

        who hates having to decipher the streetcar driver's accent just to recognize the name of his own street

        Reading that instantly reminded me of a favorite story I've related here ("a second view") before.


        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.
Re: On Solving a Simple Problem, and Appreciating the Complexity of Perl
by kaif (Friar) on Mar 05, 2006 at 17:48 UTC
    This seems like one of those places I'd appreciate a "too many arguments to printf" warning. Is there any reason against including it?
Re: On Solving a Simple Problem, and Appreciating the Complexity of Perl
by ayrnieu (Beadle) on Mar 06, 2006 at 10:12 UTC

    Simpler than scalar(): ? '*'x8 : ' 'x8

    Randomly, you will not see this problem in perl6: in it the operator x treats its LHS as a scalar and the analagous operator xx treats its LHS as a list.