FAQ
This came up somewhere else, people disagreed and now I can't see it
clearly anymore. Imagine you have an array filled with numbers, and
you only want to print out the even ones. Fine, someone says, use
grep.

print "$_\n" for grep {$_ % 2 == 0} @testAr;

But is this a void context? On the one hand you're not literally
throwing away the results, but on the other hand you're also not
looking to keep the list that grep builds itself. So in a sense, you
seem to be creating a list just to iterate over it in the print
statement. That seems to be what the Pelr FAQ has in mind here, "This
means you're making Perl go to the trouble of building a list that you
then just throw away. If the list is large, you waste both time and
space. If your intent is to iterate over the list, then use a for loop
for this purpose."

So in a context like this, would the following be better?

for my $num (@testAr) {
if (($num % 2) == 0 ) {
print "$num\n";
}
}

So I guess my question is this: Does the FAQ (and advice like it) mean
to avoid using grep *unless* you actually want to keep and use the
list, or does it just mean don't use grep only for side effects?

Thanks in advance.

Search Discussions

  • John W. Krahn at Mar 26, 2008 at 12:24 pm

    Telemachus wrote:
    This came up somewhere else, people disagreed and now I can't see it
    clearly anymore. Imagine you have an array filled with numbers, and
    you only want to print out the even ones. Fine, someone says, use
    grep.

    print "$_\n" for grep {$_ % 2 == 0} @testAr;

    But is this a void context?
    No. grep() is in list context because the for (foreach) statement
    modifier defines a list context:

    perldoc perlsyn
    [ SNIP ]
    Statement Modifiers

    Any simple statement may optionally be followed by a SINGLE
    modifier, just before the terminating semicolon (or block
    ending). The possible modifiers are:

    if EXPR
    unless EXPR
    while EXPR
    until EXPR
    foreach LIST

    On the one hand you're not literally
    throwing away the results, but on the other hand you're also not
    looking to keep the list that grep builds itself. So in a sense, you
    seem to be creating a list just to iterate over it in the print
    statement. That seems to be what the Pelr FAQ has in mind here, "This
    means you're making Perl go to the trouble of building a list that you
    then just throw away.
    You are not throwing it away, you are passing it through to the for
    statement modifier, which iterates over that list.

    If the list is large, you waste both time and
    space. If your intent is to iterate over the list, then use a for loop
    for this purpose."

    So in a context like this, would the following be better?

    for my $num (@testAr) {
    if (($num % 2) == 0 ) {
    print "$num\n";
    }
    }
    That code is iterating over a larger list, assuming that @testAr
    contains *some* odd numbers.

    So I guess my question is this: Does the FAQ (and advice like it) mean
    to avoid using grep *unless* you actually want to keep and use the
    list, or does it just mean don't use grep only for side effects?
    Void Context:

    grep {$_ % 2 == 0} @testAr;


    Scalar Context:

    my $count = grep {$_ % 2 == 0} @testAr;
    if ( grep {$_ % 2 == 0} @testAr ) {
    while ( grep {$_ % 2 == 0} @testAr ) {


    List Context:

    my @evens = grep {$_ % 2 == 0} @testAr;
    print grep {$_ % 2 == 0} @testAr;


    For grep() to produce a side effect you would have to use a
    function/operator in the conditional that produced a side effect or
    modify $_ in the conditional.

    I don't know why you would *want* to use grep() in void context that
    can't be done better some other way?



    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
  • Telemachus at Mar 26, 2008 at 1:10 pm
    On Mar 26, 8:24 am, kra...@telus.net (John W. Krahn) wrote:
    <snip>
    Telemachus wrote:
    So in a context like this, would the following be better?
    for my $num (@testAr) {
    if (($num % 2) == 0 ) {
    print "$num\n";
    }
    }
    That code is iterating over a larger list, assuming that @testAr
    contains *some* odd numbers.
    First, thanks for your response. This one bit confuses me: how is the
    if iterating over a *larger* list than the grep? Don't they both have
    to run through all of @testAr in order to weed out odd numbers? Is
    there an advantage to using grep here rather than the foreach?
    I don't know why you would *want* to use grep() in void context that
    can't be done better some other way?
    I don't want to - in fact, I want *not* to. My problem was that I
    couldn't quite see if "don't use in a void context" amounted to "don't
    use grep unless you actually wish to keep the returned items in a new
    array." I wasn't sure whether using grep to filter results for
    printing was also a no no. As my question above mentions, I'm still
    not sure whether there is any advantage to grep or to a foreach loop
    with an if-test in such a case. Or have we reached a point where it's
    just style and choice?

    Thanks.
  • Jay Savage at Mar 26, 2008 at 4:35 pm

    On Wed, Mar 26, 2008 at 9:10 AM, Telemachus wrote:
    On Mar 26, 8:24 am, kra...@telus.net (John W. Krahn) wrote:
    <snip>
    Telemachus wrote:
    So in a context like this, would the following be better?
    for my $num (@testAr) {
    if (($num % 2) == 0 ) {
    print "$num\n";
    }
    }
    That code is iterating over a larger list, assuming that @testAr
    contains *some* odd numbers.
    First, thanks for your response. This one bit confuses me: how is the
    if iterating over a *larger* list than the grep? Don't they both have
    to run through all of @testAr in order to weed out odd numbers? Is
    there an advantage to using grep here rather than the foreach?
    Not a larger list than grep, a larger list than the list of even
    numbers. @list_list_of_all_numbers > @list_of_even_numbers.

    You want to do something (print) with only certain items from a list
    that meet certain criteria. That is what grep is designed to do, so
    grep is appropriate. This isn't a void context, because you are using
    the return value. See my earlier post for some examples of grep in a
    void context actually looks like. There is nothing wrong with using
    grep to feed a while loop. It's a convenient way to turn a multi-line
    block of code into a single conditional expression, as you've done.

    If you want to see grep really shine, though, think about ways you
    might use it to avoid calling print for every element in the return
    list, e.g.

    print join "\n", grep {$_ % 2 == 0} @list;

    What you are doing now isn't wrong, though, and using a plain foreach
    loop wouldn't be wrong, either. What works best for you will depend on
    how big the list is, how your system is configured, and, most
    importantly, what is more comfortable for you and easier on whoever
    will be maintaining your program.

    This is a clear case of the the Perl motto: There Is More Than One Way To Do It.

    HTH,

    -- jay
    --------------------------------------------------
    This email and attachment(s): [ ] blogable; [ x ] ask first; [ ]
    private and confidential

    daggerquill [at] gmail [dot] com
    http://www.tuaw.com http://www.downloadsquad.com http://www.engatiki.org

    values of β will give rise to dom!
  • Rob Dixon at Mar 27, 2008 at 1:47 am

    Jay Savage wrote:

    If you want to see grep really shine, though, think about ways you
    might use it to avoid calling print for every element in the return
    list, e.g.

    print join "\n", grep {$_ % 2 == 0} @list;
    I think that's very misleading. Why should I want to avoid calling print
    for each element? What you've written is the sort of thing for which
    Perl gets a bad name - it's less readable and has no obvious benefits
    over

    print "$_\n" foreach grep { $_ % 2 == 0 } @list;

    (It also doesn't print a terminating "\n")

    Rob
  • John W. Krahn at Mar 27, 2008 at 3:07 am

    Rob Dixon wrote:

    Jay Savage wrote:
    If you want to see grep really shine, though, think about ways you
    might use it to avoid calling print for every element in the return
    list, e.g.

    print join "\n", grep {$_ % 2 == 0} @list;
    I think that's very misleading. Why should I want to avoid calling print
    for each element?
    Every function/subroutine has overhead, set up the stack, create lexical
    scope, etc. so calling print() once with a list is more efficient then
    calling print() N times in a foreach loop.

    What you've written is the sort of thing for which
    Perl gets a bad name - it's less readable and has no obvious benefits
    over

    print "$_\n" foreach grep { $_ % 2 == 0 } @list;

    (It also doesn't print a terminating "\n")
    That could be fixed thusly:

    print map {$_ % 2 == 0 ? "$_\n" : ()} @list;

    Although this would probably be more efficient:

    print map $_ & 1 ? () : "$_\n", @list;




    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
  • Jay Savage at Mar 27, 2008 at 2:02 pm

    On Wed, Mar 26, 2008 at 9:47 PM, Rob Dixon wrote:
    Jay Savage wrote:
    If you want to see grep really shine, though, think about ways you
    might use it to avoid calling print for every element in the return
    list, e.g.

    print join "\n", grep {$_ % 2 == 0} @list;
    I think that's very misleading. Why should I want to avoid calling print
    for each element? What you've written is the sort of thing for which
    Perl gets a bad name - it's less readable and has no obvious benefits
    over

    print "$_\n" foreach grep { $_ % 2 == 0 } @list;
    cmpthese(1000, {
    'foreach' => sub { print "$_\n" for grep {$_ % 2 == 0} 0..1000; },
    'join' => sub { print join "\n", grep {$_ % 2 == 0} 0..1000; },
    });

    Rate foreach join
    foreach 40.7/s -- -20%
    join 50.8/s 25% --

    25% performance gain strikes me as a pretty obvious benefit. YMMV, of
    course, depending on your data and memory performance, but in general,
    function calls are expensive, and and print moreso than most, because
    it requires a system call out to the OS. Calling a function like print
    on each iteration through a loop is rarely the most efficient way to
    do business.

    This doesn't really have anything to do with Perl; it's true in any language.

    Of course, efficiency, like beauty, is in the eye of the beholder. And
    that's why I was clear with OP, that he should do whatever made the
    most sense for him in his particular situation.

    TIMTOWTDI isn't what gives Perl a bad name; it's what makes it the
    wonderful, flexible tool that it is, and crates passionate groups of
    users like this one.
    (It also doesn't print a terminating "\n")
    I think you'll agree there are several trivial fixes for this. 'print
    "\n"' jumps immediately to mind.

    Best,

    -- jay
    --------------------------------------------------
    This email and attachment(s): [ ] blogable; [ x ] ask first; [ ]
    private and confidential

    daggerquill [at] gmail [dot] com
    http://www.tuaw.com http://www.downloadsquad.com http://www.engatiki.org

    values of β will give rise to dom!
  • Jenda Krynicky at Mar 27, 2008 at 6:08 pm

    On Wed, Mar 26, 2008 at 9:47 PM, Rob Dixon wrote:
    Jay Savage wrote:
    If you want to see grep really shine, though, think about ways you
    might use it to avoid calling print for every element in the return
    list, e.g.

    print join "\n", grep {$_ % 2 == 0} @list;
    I think that's very misleading. Why should I want to avoid calling print
    for each element? What you've written is the sort of thing for which
    Perl gets a bad name - it's less readable and has no obvious benefits
    over

    print "$_\n" foreach grep { $_ % 2 == 0 } @list;
    I do not see how is

    print join "\n", grep {$_ % 2 == 0} @list;

    any less readable than

    print "$_\n" foreach grep { $_ % 2 == 0 } @list;

    Would perhaps a pair of braces help?

    print join( "\n", grep {$_ % 2 == 0} @list);

    "I want to PRINT, JOINed by a newline, all even elements from @list"
    instead of
    "I want to print the item and a newline for each even element from
    @list". A big difference.

    Jenda
    ===== Jenda@Krynicky.cz === http://Jenda.Krynicky.cz =====
    When it comes to wine, women and song, wizards are allowed
    to get drunk and croon as much as they like.
    -- Terry Pratchett in Sourcery
  • Rob Dixon at Mar 27, 2008 at 6:23 pm

    Jay Savage wrote:
    On Wed, Mar 26, 2008 at 9:47 PM, Rob Dixon wrote:
    Jay Savage wrote:
    If you want to see grep really shine, though, think about ways you
    might use it to avoid calling print for every element in the return
    list, e.g.

    print join "\n", grep {$_ % 2 == 0} @list;
    I think that's very misleading. Why should I want to avoid calling print
    for each element? What you've written is the sort of thing for which
    Perl gets a bad name - it's less readable and has no obvious benefits
    over

    print "$_\n" foreach grep { $_ % 2 == 0 } @list;
    cmpthese(1000, {
    'foreach' => sub { print "$_\n" for grep {$_ % 2 == 0} 0..1000; },
    'join' => sub { print join "\n", grep {$_ % 2 == 0} 0..1000; },
    });

    Rate foreach join
    foreach 40.7/s -- -20%
    join 50.8/s 25% --

    25% performance gain strikes me as a pretty obvious benefit. YMMV, of
    course, depending on your data and memory performance, but in general,
    function calls are expensive, and and print moreso than most, because
    it requires a system call out to the OS. Calling a function like print
    on each iteration through a loop is rarely the most efficient way to
    do business.
    Yes I understood your intention, but efficiency isn't everything by any
    means. I believe very firmly that programs should be coded in the
    clearest and most obvious way possible, then tested and optimised if the
    performance is inadequate.The most natural way to print a list of data
    is simply to print each item, and to code it differently from that is to
    start on the road of efficiency at the cost of intelligibility. In any
    case, if someone offered me a way of making my program run in 20ms
    instead of 25ms I wouldn't be overly impressed, and certainly don't see
    it as a case of grep 'shining'.

    My guess is that optimising out print calls would be less, not more
    effective than with most subroutines: all it does is to append the
    passed-in data to the output buffer, and the system call is performed
    only when that buffer is filled.
    This doesn't really have anything to do with Perl; it's true in any
    language.
    Yes, but now you mention it I would be surprised to see this technique
    used in C:

    char buff[10000] = "\0";
    int n;
    char convert[10];

    for (n = 0; n <= 1000; n++)
    {
    sprintf(convert, "%d\n", n);
    strcat(buff, convert);
    }

    fputs(buff, stdout);


    But perhaps I am wrong.
    Of course, efficiency, like beauty, is in the eye of the beholder. And
    that's why I was clear with OP, that he should do whatever made the
    most sense for him in his particular situation.

    TIMTOWTDI isn't what gives Perl a bad name; it's what makes it the
    wonderful, flexible tool that it is, and crates passionate groups of
    users like this one.
    My concern is that Perl has a reputation for appearing to be an
    unintelligible string of symbols, and I am wary of anything that even
    leans in that direction.
    (It also doesn't print a terminating "\n")
    I think you'll agree there are several trivial fixes for this. 'print
    "\n"' jumps immediately to mind.
    Yes, of course. I was simply pointing out that they weren't equivalent.

    Cheers,

    Rob
  • Jay Savage at Mar 27, 2008 at 7:47 pm

    On Thu, Mar 27, 2008 at 2:22 PM, Rob Dixon wrote:
    Yes I understood your intention, but efficiency isn't everything by any
    means. I believe very firmly that programs should be coded in the
    clearest and most obvious way possible, then tested and optimised if the
    performance is inadequate.
    In general, I agree with you; but I maintain that "clear" and
    "obvious" are subject to interpretation. I also maintain that using
    grep to feed input to a for loop isn't obvious, whereas using grep to
    optimize out a loop is. There is nothing that grep does that can't be
    done in a loop, and we normally use grep when, for whatever reason, we
    want to avoid a loop.
    The most natural way to print a list of data
    is simply to print each item, and to code it differently from that is to
    start on the road of efficiency at the cost of intelligibility.
    Again, I don't entirely disagree. But this thread isn't about the most
    natural way to print lists. It's about grep.
    In any
    case, if someone offered me a way of making my program run in 20ms
    instead of 25ms I wouldn't be overly impressed, and certainly don't see
    it as a case of grep 'shining'.
    I think you missed my point. I may not have been clear. No, shaving a
    few ms off runtime isn't shining. But it's an *example* of the *type*
    of thing grep shines at. That is, taking loops and tunring them into
    blocks, and cutting out, in this case, 999 system calls. Are the
    savings anything to write home about? No, of course not. But the
    *idea* is fantastic, and in a more complicated situation often leads
    to not just faster execution, but saved programmer time, increased
    clarity, and simplified control structures.

    Again, it's not about a few prints in this particular made-up case.
    It's about the types of things grep really does well.
    My guess is that optimising out print calls would be less, not more
    effective than with most subroutines: all it does is to append the
    passed-in data to the output buffer, and the system call is performed
    only when that buffer is filled.
    Well, that depends entirely on what file handle you're printing to. If
    you're printing to STDOUT, as all these examples so far have, each
    newline--and therefore each pass though the loop that matches the
    condition--flushes the buffer.
    This doesn't really have anything to do with Perl; it's true in any
    language.
    Yes, but now you mention it I would be surprised to see this technique
    used in C:

    That's because C doesn't have grep. The extra variable assignments and
    sprintfs you need to make the "equivalent" C code eat up any potential
    gains. The situation of he two languages isn't at all comparable.

    TIMTOWTDI isn't what gives Perl a bad name; it's what makes it the
    wonderful, flexible tool that it is, and crates passionate groups of
    users like this one.
    My concern is that Perl has a reputation for appearing to be an
    unintelligible string of symbols, and I am wary of anything that even
    leans in that direction.
    Point well taken. But this is a thread about grep; and grep's raison
    d'etre is optimizing out loops. I don't think giving an example that
    shows grep cutting a loop is out of order. I certainly hope it wasn't
    unintelligible.

    It's also the little optimizations (like grep and map) that are built
    into the language that set Perl apart from the crowd.

    Best,

    -- jay
    --------------------------------------------------
    This email and attachment(s): [ ] blogable; [ x ] ask first; [ ]
    private and confidential

    daggerquill [at] gmail [dot] com
    http://www.tuaw.com http://www.downloadsquad.com http://www.engatiki.org

    values of β will give rise to dom!
  • Rob Dixon at Mar 27, 2008 at 9:20 pm
    Jay Savage wrote:
    [snip]
    In any case, if someone offered me a way of making my program run
    in 20ms instead of 25ms I wouldn't be overly impressed, and
    certainly don't see it as a case of grep 'shining'.
    I think you missed my point. I may not have been clear. No, shaving a
    few ms off runtime isn't shining. But it's an *example* of the *type*
    of thing grep shines at. That is, taking loops and tunring them into
    blocks, and cutting out, in this case, 999 system calls. Are the
    savings anything to write home about? No, of course not. But the
    *idea* is fantastic, and in a more complicated situation often leads
    to not just faster execution, but saved programmer time, increased
    clarity, and simplified control structures.
    Then as far as I can tell we concur!

    I thought your point was that

    print join "\n", @list, '';

    was superior to

    print "$_\n" foreach @list;

    whereas you meant something more like

    process_list(grep { $_ % 2 == 0 } @list);

    is superior to

    my @temp;
    foreach (@list) {
    push @temp, $_ if $_ % 2 == 0;
    }
    process_list(@temp);

    with which I would wholeheartedly agree :)

    I would say though that it is a shame that it was called grep. The
    utility is far from universally known, and the function doesn't provide
    a Global Regular Expression Print facility. I prefer to see it as a list
    mapping function: given an input list it returns an output list which is
    a function of the input. It can even be emulated with the map function.
    Again, it's not about a few prints in this particular made-up case.
    It's about the types of things grep really does well.
    My guess is that optimising out print calls would be less, not more
    effective than with most subroutines: all it does is to append the
    passed-in data to the output buffer, and the system call is performed
    only when that buffer is filled.
    Well, that depends entirely on what file handle you're printing to. If
    you're printing to STDOUT, as all these examples so far have, each
    newline--and therefore each pass though the loop that matches the
    condition--flushes the buffer.
    [snip]

    Now there I would disagree. Only STDERR is autoflushed: data sent to
    STDOUT is output only when the buffer fills, unless that is explicitly
    changed.

    Cheers,

    Rob
  • Jay Savage at Mar 26, 2008 at 1:22 pm

    On Tue, Mar 25, 2008 at 11:00 PM, Telemachus wrote:
    This came up somewhere else, people disagreed and now I can't see it
    clearly anymore. Imagine you have an array filled with numbers, and
    you only want to print out the even ones. Fine, someone says, use
    grep.

    print "$_\n" for grep {$_ % 2 == 0} @testAr;

    But is this a void context? On the one hand you're not literally
    throwing away the results, but on the other hand you're also not
    looking to keep the list that grep builds itself. So in a sense, you
    seem to be creating a list just to iterate over it in the print
    statement. That seems to be what the Pelr FAQ has in mind here, "This
    means you're making Perl go to the trouble of building a list that you
    then just throw away. If the list is large, you waste both time and
    space. If your intent is to iterate over the list, then use a for loop
    for this purpose."

    So in a context like this, would the following be better?

    for my $num (@testAr) {
    if (($num % 2) == 0 ) {
    print "$num\n";
    }
    }

    So I guess my question is this: Does the FAQ (and advice like it) mean
    to avoid using grep *unless* you actually want to keep and use the
    list, or does it just mean don't use grep only for side effects?

    Thanks in advance.
    No, you're using grep in list context. You *are* keeping the results
    (from grep's perspective): you're passing the result list to for.

    Remember, though, that grep evaluates the code in the block, once for
    each time through the list, so in theory, you could do things like:

    grep {print "$_\n"} 0..1000;
    grep {$count++ if $_ % 2 == 0} 0..1000;
    grep {$_++} @nums;

    In these examples, we really are just ignoring the return value of
    grep entirely; that is what the docs mean by using grep in a void
    context. Don't do that. grep spends memory and processor cycles
    building up the result list; don't waste those resources if you don't
    need the results.

    If all you want to do is iterate through a list and do something with
    each of the elements, there are other, more efficient, ways to do
    that. The examples above could be better solved by while loops, or
    map, depending on the context.

    In your case, though, you really do need the results to pass to for,
    so grep is the correct tool.

    HTH,

    -- jay
    --------------------------------------------------
    This email and attachment(s): [ ] blogable; [ x ] ask first; [ ]
    private and confidential

    daggerquill [at] gmail [dot] com
    http://www.tuaw.com http://www.downloadsquad.com http://www.engatiki.org

    values of β will give rise to dom!

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupbeginners @
categoriesperl
postedMar 26, '08 at 3:02a
activeMar 27, '08 at 9:20p
posts12
users5
websiteperl.org

People

Translate

site design / logo © 2022 Grokbase