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"

In reply to On Solving a Simple Problem, and Appreciating the Complexity of Perl by liverpole

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.