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

In light of Junkward Wars, Perl Style, I offer the following puzzle which limits what you have to work with.

In this case, your task is to write a subroutine that takes in a reference to a list of numbers and returns the average of those numbers, with the limitations that:


Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain

Replies are listed 'Best First'.
Re (tilly) 1: "Junkyard" Puzzle: Average of Numbers
by tilly (Archbishop) on May 13, 2001 at 06:17 UTC
    First of all, Perl is list-oriented and handles passed in arguments by reference. Therefore the Perlish way to do this is to just take a list of numbers and work with that. But I will play off the rule as given even though it is not the API I would choose.

    Also the stricture to avoid having any variables should not be looked at as just a bizarre restriction. People who program in a functional style often do that voluntarily. With that in mind I offer the following version that follows the restrictions as a matter of intentional style and just happens to solve a more complex problem. The function is avg_nested which extracts the numbers from a nested set of arrays of arrays and computes the average of the entire list:

    sub avg { sum(@_) / @_; } sub avg_nested { avg(flatten_arrays(@_)); } sub flatten_arrays { map {ref($_) ? flatten_arrays(@$_) : $_} @_; } sub sum { @_ ? shift(@_) + sum(@_) : 0; }

    UPDATE
    jynx caught me. I wrote it, then changed a function name at the last minute and didn't test properly. I forgot to change one flatten to flatten_arrays...

      First of all, Perl is list-oriented and handles passed in arguments by reference. Therefore the Perlish way to do this is to just take a list of numbers and work with that. But I will play off the rule as given even though it is not the API I would choose.

      I know this is the case, what I was trying to prevent doing was giving the 'player' the opporuntity to use the copy of the list if the list was passed by value; they could modify this with no concern over altering the original array. (So, for example, doing something that stores the sum in some place in the list copy, would be possible). However, with passing by reference, any changes to the sub's array would affect the original array, and thus you are forced to find another means to store the value.


      Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
Re: "Junkyard" Puzzle: Average of Numbers
by no_slogan (Deacon) on May 13, 2001 at 05:49 UTC
    eval(join "+", @{$_[0]}) / @{$_[0]}

    Update: Oh, are we golfing? That's 31 characters without the spaces.

      Damn! I did this one without reading a single other post and came out with a sloppier version that yours, but along the same lines (I'm new at this :)
      sub find_avg { (eval join"+",@{$_[0]})/(($#{$_[0]})+1); }
      I had a hell of a time getting $#array working with $_[0]. I see I over complicated it :)
      I'd like to add that these "Junkyard" puzzles, Golf and such are extremely good practice for those of us lower on the curve. Just in the past week or so, I've added quite a bit of knowledge to my base. These remind me much of doing geometric proofs back in middle school. Entertaining as well as educational.

      Thank you.

      I wouldn't suggest golfing with these. Sure, you could, but that's not the challenge; it's probably more interesting to see how many TIMTOWTDI approaches can be taken.
      Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
Re: "Junkyard" Puzzle: Average of Numbers
by jmcnamara (Monsignor) on May 14, 2001 at 02:04 UTC

    This is 37 chars. I saw Abigail do something like this on clpmisc:
    sub average1 { local$"="+";eval("@{$_[0]}")/@{$_[0]} }
    This looks prettier when dealing with an ordinary list:
    sub average2 { local$"="+";eval("@_")/@_ }
    In which case this would be shorter:
    sub average3 { eval(join"+",@_)/@_ }
    The last one is the same as no_slogan's.

    John.
    --
Re: "Junkyard" Puzzle: Average of Numbers
by repson (Chaplain) on May 13, 2001 at 05:42 UTC
    Here's the first thing that springs to mind if I read the problem correctly (30 characters):
    sub avg { (map{(1)x$_}@{$_[0]})/@{$_[0]} }
      Clever, but the problem statement doesn't guarantee that we're dealing with non-negative integers.
Re: "Junkyard" Puzzle: Average of Numbers (Newbie approach)
by bambam on May 13, 2001 at 06:09 UTC

    Here's a newbie approach w/o using  eval : ).

    sub s{ push @{$_[0]},0; ${$_[0]}[$#{$_[0]}] += ${$_[0]}[$_] for 0..$#{$_[0]}-1; ${$_[0]}[$#{$_[0]}] /= $#{$_[0]}; pop @{$_[0]}; }
Re: "Junkyard" Puzzle: Average of Numbers
by DrZaius (Monk) on May 13, 2001 at 12:22 UTC
    Here is my attempt:

    sub avg { return eval '(' . join('+', @{$_[0]}) . ')/' . scalar(@{$_[0]}); }

    Works pretty good over all :)

      Hi,

      You could make it shorter by taking out the scalar(). @{$_[0]} is already interpretted in scalar context.

      $code or die
      $ perldoc perldoc
Re: "Junkyard" Puzzle: Average of Numbers
by $code or die (Deacon) on May 13, 2001 at 22:25 UTC
    sub average { map{$_[1]+=$_}@{$_[0]};$_[1]/@{$_[0]} }
    Works under strict and warnings. Although I am probably breaking your second rule (creating $_[1]).
    $code or die


    $ perldoc perldoc
Re: "Junkyard" Puzzle: Average of Numbers
by MeowChow (Vicar) on May 13, 2001 at 22:56 UTC
    Flibbertigibbit! All the fun ones have been done...
    use List::Util qw(sum); sub avg { (sum @{$_[0]}) / @{$_[0]} }
    oops: didn't notice the last rule. never mind, run along now...
       MeowChow                                   
                   s aamecha.s a..a\u$&owag.print