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.
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.
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 :-("
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.
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.
perlmonks.org content © perlmonks.org and Anonymous Monk, ayrnieu, BrowserUk, kaif, liverpole, Scott7477, zentara
prlmnks.org © 2006 edmund von der burg (eccles & toad)
v 0.03