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

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

Hello, I have started to work through the Intermediate Perl book after the Learning Perl book. I really enjoyed the Learning Perl book, it was pretty clear and readable. The Intermediate seems less clear and I am already stuck in the second chapter on a grep example:

```my @odd_digit_sum = grep digit_sum_is_odd(\$_), @input numbers;

sub digit_sum_is_odd {
my \$input = shift;
my @digits = split //, \$input;
my \$sum;
\$sum += \$_ for @digits;
return \$sum % 2;
}

The book gives @input_numbers = (1, 2, 4, 8, 16, 32, 64), and says that the @odd_digit_sum resulting from the grep code will give 1, 16, and 32. I must be an idiot, because I do not understand this example( and the book really does not explain it). If grep places each value of @input_numbers into \$_ one at a time, how does it ever sum anything??

I thought maybe it calls the subroutine with the entire list (even though this is not how grep is supposed to work), but even then, the answer of 1, 16 and 32 does not make sense to me. I know grep will give the answers when sum % 2 is not zero, but if you start with 1, then add 2, you get 3 which gives non-zero result, so grep should give 2 also. Still do not understand how it sums anything, given that \$_ gets a single value one at a time?! Really confused, any help would be appreciated!! Thanks!

Replies are listed 'Best First'.
Re: do not understand grep example
by BrowserUk (Patriarch) on Jun 15, 2012 at 17:38 UTC

The grep line my @odd_digit_sum = grep digit_sum_is_odd(\$_), @input numbers;

passes each number into the subroutine using \$_. Within the subroutine that value is assigned to \$input:

```my \$input = shift;

The digits of each number are then split into the array @digits

```my @digits = split //, \$input;

At this point, if \$input is 32; then @digits contains: ( '3', '2' ).

Those digits are then summed

```\$sum += \$_ for @digits;

giving 5, and then tested to see if the result is odd:

```return \$sum % 2;

Which in the case of 32 is true, so true is returned to grep and grep allows that input (32) through to the results array.

With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.

The start of some sanity?

Thank you all for your responses, the Monastery has not failed me yet!

BrowserUk, your response provided me with the answer. I realized that my issue was not with grep, but rather with split. I did not realize that split using null (split //) would split by character (e.g. "32" would become 3, 2).

Now I see when the summing takes place. The book glossed over this, but it would have been nice if it had just noted that to jog my memory, which is not so great anymore.

Thank you!
Re: do not understand grep example
by moritz (Cardinal) on Jun 15, 2012 at 17:29 UTC
thought maybe it calls the subroutine with the entire list (even though this is not how grep is supposed to work),

No, it does not. It calls the subroutine for every value in the list, and only returns if the subroutine returns a true value.

So you could also write

```my @odd_digit_sum = grep digit_sum_is_odd(\$_), @input numbers;

As

```my @odd_digit_sum;
for (@input) {
if (digit_sum_is_odd(\$_)) {
push @odd_digit_sum, \$_;
}
}

and it would return the same result. That's what grep does: it goes through the list, calls the function for every value, and collect those values for which the function returned something true.

Re: do not understand grep example
by tobyink (Canon) on Jun 15, 2012 at 18:45 UTC

Forget about the grep for a while and just get straight in your head how the digit_sum_is_odd function works and what it does.

```use Test::More tests => 14;

sub digit_sum_is_odd {
my \$input = shift;
my @digits = split //, \$input;
my \$sum;
\$sum += \$_ for @digits;
return \$sum % 2;
}

ok     digit_sum_is_odd(1);
ok not digit_sum_is_odd(2);
ok     digit_sum_is_odd(3);
ok     digit_sum_is_odd(9);
ok     digit_sum_is_odd(10);
ok not digit_sum_is_odd(11);
ok     digit_sum_is_odd(12);
ok not digit_sum_is_odd(13);
ok     digit_sum_is_odd(1000000);
ok not digit_sum_is_odd('pony');
ok     digit_sum_is_odd('pony1');
ok     digit_sum_is_odd(3, 4);
ok     digit_sum_is_odd(3, 5);
ok     digit_sum_is_odd(3, 'chimpanzee', 'monkey', 'baboon');