http://www.perlmonks.org?node_id=962924

tangent has asked for the wisdom of the Perl Monks concerning the following question:

Venerable Monks, I came across this error recently and was wondering if someone could explain it to me:
use strict; use warnings; my %hash = (abc=>1,def=>1); my @array = qw/abc def ghi/; my @ary1 = grep{ ! $hash{$_} } @array; # OK my @ary2 = grep( ! $hash{$_}, @array); # OK my @ary3 = grep{ not $hash{$_} } @array; # OK my @ary4 = grep( not $hash{$_}, @array); # NOT OK Errors from fourth statement: Useless use of hash element in void context Not enough arguments for grep near "@array)"
From perldoc:
Unary "not" returns the logical negation of the expression to its right. It's the equivalent of "!" except for the very low precedence.

Replies are listed 'Best First'.
Re: Logical Not and arguments to grep
by chromatic (Archbishop) on Apr 01, 2012 at 20:40 UTC

    The comma operator has a higher precedence than the not operator. The list of two expressions (the hash element and the array) has to be evaluated before not can be evaluated; that's the source of the void context warning.

    I almost always use map with a block so I never have to think about this sort of thing.


    Improve your skills with Modern Perl: the free book.

      Thanks chromatic, that explains it. I've never really considered the comma as an "operator" but will do from now.
        I've never really considered the comma as an "operator" but will do from now.

        Neither did I for many years. Once you really understand how lists work in Perl (and why some people call the comma "the list operator"), then you understand enough about Perl's precedence and associativity that this sort of thing never trips you up much again.

        Working through the exercises in the first chapter of Structure and Interpretation of Computer Programs (and working on a couple of parsers and compilers) cemented this knowledge for me. Fortunately, there's a shortcut: just remember that your computer prefers to do one thing at a time. Not everything can happen at once. There's a sequence to which operations happen.

        Once you get that, you can reason your way to the need for associativity and precedence.

        I also prefer to use the block form of map and grep.

        The comma operator is useful in other ways..see the command loop I coded at Re: Storing 10 numbers in array. In the statement,

        while ( (print "$prompt: "), $number = <STDIN> ) {...}
        The comma operator allows this to be a single statement.
        You can't put two statements like:
        print "$prompt"; $number = <STDIN>;
        into the while() conditional. But this statement which uses the comma operator is ok. The "truth or falseness" of the statement is decided by the last clause (the input of the data - not the return value of the print which will be a "1"). Note the paren's are required.

        By doing it this way, a re-prompt happens automatically at every loop iteration without having to code the prompting statement within the loop (and having a standalone initial prompting statement before the loop to get things started).

Re: Logical Not and arguments to grep
by ikegami (Patriarch) on Apr 02, 2012 at 19:27 UTC

    To override precedence, parens are usually used.

    my @ary5 = grep( ( not $hash{$_} ), @array); # OK
Re: Logical Not and arguments to grep ( not( $hash{$_} ) )
by Anonymous Monk on Jun 05, 2013 at 08:10 UTC

    In the spirit of parentheses you could use not( $hash{$_} )

    $ perl -MO=Deparse,-p -le " print grep( not $foo{$_} , @bar )" Not enough arguments for grep at -e line 1, near "@bar ) " -e had compilation errors. BEGIN { $/ = "\n"; $\ = "\n"; } print(grep); $ perl -MO=Deparse,-p -le " print grep( !$foo{$_} , @bar )" BEGIN { $/ = "\n"; $\ = "\n"; } print(grep((!$foo{$_}), @bar)); -e syntax OK $ perl -MO=Deparse,-p -le " print grep( not( $foo{$_}) , @bar )" BEGIN { $/ = "\n"; $\ = "\n"; } print(grep((!$foo{$_}), @bar)); -e syntax OK