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

There's probably all sorts of things wrong with this code stylistically, but I'm just playing around with stuff.

#! perl -w use strict; local ($,=" - ", $\="\n"); my ($ave, @left, @right); my @array = qw/ 1 2 3 4 5 6 7 8 9 10/; $ave = do { $ave += $_ for (@array) } / scalar @array; print $ave, int($ave); # << Both these print 0 (zero) ....... for (@array) { last if $_ < int($ave); # << ...but the array <b>is</b> being s +plit at the mean value. push( @left, shift @array ); } print @left; print @array; __DATA__ #output C:\test>180278 0 - 0 1 - 2 - 3 - 4 - 5 6 - 7 - 8 - 9 - 10

What is going on with $ave?

If anyone wants to offer a better way of doing this I am all ears but I would still appreciate an explanation of why $ave prints as 0 but obviously has some value other than zero else the loop would terminate leaving all the values in @array?

Note: The test in the last if... is reversed from what (I think) it should be, but using > exits the loop immediately.

Thanks to you if you can lift the veil.

ps. Sorry dws, but I can't think of a more descriptive title for this?


Anyone know of an abbottoire going cheap?

Replies are listed 'Best First'.
Re: Strangness with arrays
by japhy (Canon) on Jul 10, 2002 at 20:12 UTC
    Your first problem is your do { ... }. It is returning 0, because the last thing evaluated is NOT the $ave += $_, but rather the false value returned by for to indicate it's finished. $ave = do { $ave += $_ for @array; $ave } / @array; is a fix, or (if you don't mind lots of divisions): $ave += $_/@array for @array; So no, $ave does NOT have a different value elsewhere in your program -- your array happens to be perfectly distributed, and if you used (1, 1, 2, 3, 5, 8, 13, 21, 34), you'd find you get bizarre different results.

    The next problem is you meddling with the array while you loop over it. Don't. Do something like this instead: push @{ $_ < $ave ? \@lower : \@higher }, $_ for @array;

    _____________________________________________________
    Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a job (NYC-area)
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

      Thanks japhy for both the explainations and the especially the alternatives.


      Anyone know of an abbottoire going cheap?

Re: Strangness with arrays
by chromatic (Archbishop) on Jul 10, 2002 at 19:55 UTC
    It's generally considered bad form to change the size of an array while you're iterating through it. You try walking while someone else keeps replacing your boots. I doubt that last is having any effect -- it's more likely that the array runs out of elements after five passes.

      Your right. commenting out the last if.. results in identical output. The fact that it results in the array being split exactly at the place I wanted (with this data set) is purely coincidental. How lucky can you be? (Might be an interesting quirk to use in an obfu sometime).

      I guess I should have recognised something was up when I noticed the if condition was inverted...as I typed up the question, but I didn't :(

      I am amazed how two non-vocal errors can conspire to apparently deliver exactly the output that I require! I shouldn't be, I've seen it before a few times, but I am anyway!

      Thanks.


      Anyone know of an abbottoire going cheap?

Re: Strangness with arrays
by Fletch (Bishop) on Jul 10, 2002 at 19:55 UTC

    Modifying arrays (well, destructively modifiying them by changing their size) as you're iterating over them with for() is usually asking for trouble. Quoting from perldoc perlsyn:

    If any part of LIST is an array, "foreach" will get very confused if you add or remove elements within the loop body, for example with "splice". So don't do that.
Re: Strangness with arrays
by Elian (Parson) on Jul 10, 2002 at 20:27 UTC
    The problem is with the
    for (@array)
    bit. Under normal circumstances, the array is flattened into a list and you can then mess with @array safely. However... there's an optmization in perl such that if the only thing in the list is a plain array then perl doesn't flatten, it just whips through the array, and modifying the array has... Interesting Results.

    If you want, you can throw another set of parens around @array to force it to be a list, then you can mess with it as much as you like within the loop without really messing things up.

      Actually, the loop variable is always aliased to the loop elements, even when you say something like for (@a1, @a2, @a3) or even for (qw(foo bar baz 1 2 3), @a1). If you really want to force flattening, you need to force a copy of the data structure: for(@{[@a1, @a2, @a3]})

      Update: point taken.

      Makeshifts last the longest.

        You misunderstood.

        Yes, the loop variable always aliases to the individual scalars. This is true. But in the case presented, with a bare, single array in for/foreach's list, perl also turns the loop into an iteration over the array itself, rather than flattening the array and iterating over the list of scalars.

Re: Strangness with arrays
by Aristotle (Chancellor) on Jul 10, 2002 at 20:38 UTC
    japhy++ for the right explanation. I just want to add that when you get strange behaviour, your first thought should be to alter your test case. Generate other sequences. Use rand(). Try to see when it behaves the way you expect it to, or don't.

    Makeshifts last the longest.

      Good suggestion well taken. My only (pathetic)defense is, that bit was working........

      Granted, I couldn't explain why it was working but, but.............

      {hangs head in shame!}


      Anyone know of an abbottoire going cheap?

        No shame.. everyone makes boneheaded mistakes. It took me a couple of those before I understood that same advice I just gave you, and I sometimes still have to remind myself that it may only be a coincidence with the chosen test case.

        Pragmatic lesson I took home: when testing code, generate two random and reasonably large test cases to run it against. Even if one of the data sets happens to display strange behaviour, odds that they both do in the same way are rather low.

        Makeshifts last the longest.