Home > Archive > PERL Beginners > March 2008 > commify_series script in cookbook page 94
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
commify_series script in cookbook page 94
|
|
| Richard Lee 2008-03-30, 7:27 pm |
|
While reading perl cookbook, I came to page 94 and having a hard time
understanding this particular phrase
my $sepchar = grep( /,/ => @_ ) ? ";" : ",";
I recognize the ternary operator and grep but I am not sure how they are
forming the meaning together.
I thought grep needed lists to work on ? and grep(/,/ => @_), I am not
sure where it's getting the list from
my @results = grep EXPR, @input_list;
my $count = grep EXPR, @input_list;
And I also don't understand what ";" is doing in the ternary operator??
I think I understand the rest of the program though..
Can someone help me out please?
thank you.
-------------------- Complete program --------------------------
#!/usr/bin/perl -w
use strict;
my @lists = (
[ 'just one thing' ],
[ qw(Mutt Jeff) ],
[ qw(peter Paul mary) ],
[ 'To our parents', 'Mother Theressa', 'God' ],
[ 'pastrami', 'ham and cheese', 'peanut butter and jelly', 'tuna' ],
[ 'recycle tired, old phrases', 'ponder big, happy thoughts' ],
[ 'recycle tired, old phrases',
'ponder big, happy thoughts',
'sleep and dream peacefully' ],
);
for my $aref ( @lists ) {
print "The list is: ". commify_series( @$aref ) . ".\n";
}
sub commify_series {
my $sepchar = grep( /,/ => @_ ) ? ";" : ",";
( @_ == 0 ) ? ''
: ( @_ == 1 ) ? $_[0]
: ( @_ == 2 ) ? join(" and ", @_ )
: join("$sepchar ", @_[0 .. ($#_-1) ], "and $_[-1]" );
}
| |
| John W. Krahn 2008-03-30, 7:27 pm |
| Richard Lee wrote:
>
> While reading perl cookbook, I came to page 94 and having a hard time
> understanding this particular phrase
>
> my $sepchar = grep( /,/ => @_ ) ? ";" : ",";
>
> I recognize the ternary operator and grep but I am not sure how they are
> forming the meaning together.
>
> I thought grep needed lists to work on ? and grep(/,/ => @_), I am not
> sure where it's getting the list from
>
> my @results = grep EXPR, @input_list;
> my $count = grep EXPR, @input_list;
>
> And I also don't understand what ";" is doing in the ternary operator??
Inside of a subroutine the @_ contains the arguments supplied to that
subroutine so the list comes from @_. grep() is used in boolean context
so if any of the contents of @_ contains a comma then ";" is assigned to
$sepchar else if there are no commas then "," is assigned to $sepchar.
> I think I understand the rest of the program though..
>
> Can someone help me out please?
>
> thank you.
>
> -------------------- Complete program --------------------------
> #!/usr/bin/perl -w
>
> use strict;
>
> my @lists = (
> [ 'just one thing' ],
> [ qw(Mutt Jeff) ],
> [ qw(peter Paul mary) ],
> [ 'To our parents', 'Mother Theressa', 'God' ],
> [ 'pastrami', 'ham and cheese', 'peanut butter and jelly', 'tuna' ],
> [ 'recycle tired, old phrases', 'ponder big, happy thoughts' ],
> [ 'recycle tired, old phrases',
> 'ponder big, happy thoughts',
> 'sleep and dream peacefully' ],
> );
>
> for my $aref ( @lists ) {
> print "The list is: ". commify_series( @$aref ) . ".\n";
> }
>
> sub commify_series {
> my $sepchar = grep( /,/ => @_ ) ? ";" : ",";
> ( @_ == 0 ) ? ''
> : ( @_ == 1 ) ? $_[0]
> : ( @_ == 2 ) ? join(" and ", @_ )
> : join("$sepchar ", @_[0 .. ($#_-1) ], "and $_[-1]" );
> }
If @_ is empty then return '' else if @_ contains one element return
that element else if @_ contains two elements then join them both with
the string " and " else join the first through second to last, and the
last prepended with the string "and ", together using the string
"$sepchar ".
John
--
Perl isn't a toolbox, but a small machine shop where you
can special-order certain sorts of tools at low cost and
in short order. -- Larry Wall
| |
| Yitzle 2008-03-30, 7:27 pm |
| > my $sepchar = grep( /,/ => @_ ) ? ";" : ",";
> And I also don't understand what ";" is doing in the ternary operator??
The ";" is what is returned if the condition is TRUE.
"( / ,/ => @_)" is a(n anonymous) hash. I've had no idea that grep can
operate on a hash.
However, it seems that "grep( / ,/ => @_)" and "grep( / ,/, @_)" are equivalent.
__CODE__
@a = qw/aa ab ba bb/;
print join(", ", grep(/a/, @a) );
print "\n";
print join(", ", grep(/a/ => @a) );
print "\n";
__END__
aa, ab, ba
aa, ab, ba
@_ is a list/array, and that is what grep is searching for the RegEx / ,/.
@_ is the arguments passed into the current function/sub.
In this context, grep is being used in a scalar context. So it returns
the number of times that "the expression is true", ie the number of
elements that contain a " ,". In a TRUE/FALSE context, it returns
whether any of the passed arguments have a " ,". If there is a " ," in
one of the arguments, the $sepchar is set to ";". Otherwise it is set
to ",".
HTH!
| |
| Rob Dixon 2008-03-30, 7:27 pm |
| Richard Lee wrote:
>
> While reading perl cookbook, I came to page 94 and having a hard time
> understanding this particular phrase
>
> my $sepchar = grep( /,/ => @_ ) ? ";" : ",";
>
> I recognize the ternary operator and grep but I am not sure how they are
> forming the meaning together.
>
> I thought grep needed lists to work on ? and grep(/,/ => @_), I am not
> sure where it's getting the list from
>
> my @results = grep EXPR, @input_list;
> my $count = grep EXPR, @input_list;
>
> And I also don't understand what ";" is doing in the ternary operator??
>
> I think I understand the rest of the program though..
>
> Can someone help me out please?
The => operator is misleading - it's exactly equivalent to a comma in
this case, so
grep(/,/, @_)
(in scalar context) returns the number of elements of @_ that contain a
comma character. Either comma or semicolon is assigned to $sepchar
according to whether that count is false (zero) or true (non-zero).
So, in plain English, if any of the elements of @_ contain a comma then
$sepchar is set to a semicolon; otherwise it is set to a comma.
HTH,
Rob
| |
| John W. Krahn 2008-03-30, 7:28 pm |
| yitzle wrote:
>
> The ";" is what is returned if the condition is TRUE.
>
> "( / ,/ => @_)" is a(n anonymous) hash.
No it is not. /,/ and @_ are just two arguments to the grep() function.
> I've had no idea that grep can
> operate on a hash.
> However, it seems that "grep( / ,/ => @_)" and "grep( / ,/, @_)" are equivalent.
Yes, '=>' and ',' are just different versions of the comma operator.
> __CODE__
> @a = qw/aa ab ba bb/;
> print join(", ", grep(/a/, @a) );
> print "\n";
> print join(", ", grep(/a/ => @a) );
> print "\n";
> __END__
> aa, ab, ba
> aa, ab, ba
>
> @_ is a list/array, and that is what grep is searching for the RegEx / ,/.
perldoc -q "What is the difference between a list and an array"
And the regexp in the example is /,/ not / ,/.
John
--
Perl isn't a toolbox, but a small machine shop where you
can special-order certain sorts of tools at low cost and
in short order. -- Larry Wall
| |
| Richard Lee 2008-03-30, 7:28 pm |
| Rob Dixon wrote:
> Richard Lee wrote:
>
>
> The => operator is misleading - it's exactly equivalent to a comma in
> this case, so
>
> grep(/,/, @_)
>
> (in scalar context) returns the number of elements of @_ that contain a
> comma character. Either comma or semicolon is assigned to $sepchar
> according to whether that count is false (zero) or true (non-zero).
>
> So, in plain English, if any of the elements of @_ contain a comma then
> $sepchar is set to a semicolon; otherwise it is set to a comma.
>
> HTH,
>
> Rob
>
thanks guys!!
Understood them.. moving on.....
| |
| Dr.Ruud 2008-03-30, 7:28 pm |
| Richard Lee schreef:
> While reading perl cookbook, I came to page 94 and having a hard time
> understanding this particular phrase
>
> my $sepchar = grep( /,/ => @_ ) ? ";" : ",";
Shortcutting alternative:
my $sepchar = ",";
for (@_) { /,/ and $sepchar = ";" and last }
but (unless @_ is quite big) the grep alternative is likely to be
faster.
--
Affijn, Ruud
"Gewoon is een tijger."
| |
| Richard Lee 2008-03-31, 4:37 am |
| Dr.Ruud wrote:
> Richard Lee schreef:
>
>
>
> Shortcutting alternative:
>
> my $sepchar = ",";
> for (@_) { /,/ and $sepchar = ";" and last }
>
> but (unless @_ is quite big) the grep alternative is likely to be
> faster.
>
>
I understood the original but I am not so sure of your solution??
Can you write it out completely so that I can try it out?
thanks.
| |
| Rob Dixon 2008-03-31, 8:29 am |
| Richard Lee wrote:
> Dr.Ruud wrote:
> I understood the original but I am not so sure of your solution??
>
> Can you write it out completely so that I can try it out?
It's equivalent to:
my $sepchar = ',';
foreach (@_) {
if (/,/) {
$sepchar = ';';
last;
}
}
And IMO is much better written that way.
HTH,
Rob
| |
| Dr.Ruud 2008-03-31, 7:55 pm |
| Rob Dixon schreef:
> Richard Lee wrote:
[color=darkred]
>
> It's equivalent to:
>
> my $sepchar = ',';
> foreach (@_) {
> if (/,/) {
> $sepchar = ';';
> last;
> }
> }
>
> And IMO is much better written that way.
TIMTOWTDI.
Another shortcutting alternative:
my $sepchar = ',';
for (@_) { $sepchar = ";" and last if /\Q$sepchar/ }
And another:
use List::Util qw(first);
my $sepchar = first( {/,/} @_ ) ? ";" : ",";
And another:
use List::MoreUtils qw(any);
my $sepchar = any( {/,/} @_ ) ? ";" : ",";
But, as already stated, often this is fine:
my $sepchar = grep(/,/, @_) ? ";" : ",";
--
Affijn, Ruud
"Gewoon is een tijger."
| |
| Rob Dixon 2008-03-31, 7:56 pm |
| Dr.Ruud wrote:
> Rob Dixon schreef:
>
>
> TIMTOWTDI.
But I'm sure you'll agree that some ways are more awkward or obfuscated
than others.
> Another shortcutting alternative:
>
> my $sepchar = ',';
> for (@_) { $sepchar = ";" and last if /\Q$sepchar/ }
This relies on ';' being true, and uses 'and' in void context.
$sepchar = '' and last if ...
wouldn't work; or, rather, it would work but wouldn't exit from the loop
when intended.
> And another:
>
> use List::Util qw(first);
> my $sepchar = first( {/,/} @_ ) ? ";" : ",";
This relies on an element matching /,/ being true, and first()
unnecessarily returns the element value when all that is required is a
boolean.
> And another:
>
> use List::MoreUtils qw(any);
> my $sepchar = any( {/,/} @_ ) ? ";" : ",";
Spot on. I believe that's the ideal solution as everything is being used
for its intended purpose. any() is required to return only a boolean
value, and it can be implemented so that it stops searching as soon as
the condition is satisfied, whereas grep is obliged to count right to
the bitter end. It's a shame that MoreUtils isn't a standard module.
> But, as already stated, often this is fine:
>
> my $sepchar = grep(/,/, @_) ? ";" : ",";
It is the usual way, yes. It has the huge advantage that it does what it
says it does (apart from grep being an awful name for a function).
Rob
| |
| Chas. Owens 2008-03-31, 7:58 pm |
| On Mon, Mar 31, 2008 at 2:30 PM, Rob Dixon <rob.dixon@gmx.com> wrote:
snip
>
> This relies on ';' being true, and uses 'and' in void context.
>
snip
There is nothing wrong with using and in void context. If you have a
problem with it then you have a problem with many Perl idioms such as
open my $fh, "<", $file
or die "could not open $file: $!";
This uses or in void context. The use of and and or to control flow
is a long standing feature of the language. Perhaps you are confusing
the use of grep and map in a void context being bad. They are bad in
a void context because they are inefficient compared to the equivalent
for loops in some versions of Perl (prior to 5.8.1) and they have no
benefits in comparison with the for loop (it isn't shorter or more
clear).
snip
> $sepchar = '' and last if ...
>
> wouldn't work; or, rather, it would work but wouldn't exit from the loop
> when intended.
snip
Look at the intent of the piece of code again. Having an empty string
for a separator would be meaningless (and would break the regex). A
better example would be "0", but even that is pushing it since zero is
not a traditional separator character.
--
Chas. Owens
wonkden.net
The most important skill a programmer can have is the ability to read.
| |
| John W. Krahn 2008-03-31, 7:59 pm |
| Rob Dixon wrote:
> Dr.Ruud wrote:
>
> But I'm sure you'll agree that some ways are more awkward or obfuscated
> than others.
>
>
> This relies on ';' being true, and uses 'and' in void context.
"'and' in void context"? 'and' has two operands so how is it in void
context?
John
--
Perl isn't a toolbox, but a small machine shop where you
can special-order certain sorts of tools at low cost and
in short order. -- Larry Wall
| |
| Rob Dixon 2008-03-31, 7:59 pm |
| John W. Krahn wrote:
> Rob Dixon wrote:
>
> "'and' in void context"? 'and' has two operands so how is it in void
> context?
The operands are nothing to do with the context. The compiler doesn't
complain about 'and', but for addition:
($sepchar = ";") + last if /\Q$sepchar/;
we get
Useless use of addition (+) in void context...
Rob
| |
| Rob Dixon 2008-03-31, 8:00 pm |
| Chas. Owens wrote:
> On Mon, Mar 31, 2008 at 2:30 PM, Rob Dixon <rob.dixon@gmx.com> wrote:
> snip
> snip
>
> There is nothing wrong with using and in void context. If you have a
> problem with it then you have a problem with many Perl idioms such as
>
> open my $fh, "<", $file
> or die "could not open $file: $!";
>
> This uses or in void context. The use of and and or to control flow
> is a long standing feature of the language. Perhaps you are confusing
> the use of grep and map in a void context being bad. They are bad in
> a void context because they are inefficient compared to the equivalent
> for loops in some versions of Perl (prior to 5.8.1) and they have no
> benefits in comparison with the for loop (it isn't shorter or more
> clear).
No there is no confusion, and I also have a problem with your example
with open: 'and' and 'or' are operators and return values that should be
used. It makes just as much sense to write
($sepchar = ';') + last if /\Q$sepchar/
Having said that I always use that idiom with open() because IMO the
alternative using 'if' is more clumsy and it's the best way to do that
in Perl. It also reads properly as English.
OTOH
$sepchar = ';' and last
also reads as English but doesn't do what it says it does. I would go
with the comma operator though:
$sepchar = ';', last
is fine.
> snip
> snip
>
> Look at the intent of the piece of code again. Having an empty string
> for a separator would be meaningless (and would break the regex). A
> better example would be "0", but even that is pushing it since zero is
> not a traditional separator character.
My misgivings were precisely that the construct would work only in that
context. Somebody could be forgiven for thinking that, based on the
advice, something like
$idx = 0 and $string = '' and next;
would also work.
Rob
|
|
|
|
|