Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

"Junkyard" Puzzle: Average of Numbers

by Masem (Monsignor)
on May 13, 2001 at 05:00 UTC ( [id://79981]=perlmeditation: print w/replies, xml ) Need Help??

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:

  • You cannot modify the array elements themselves, or if you do, the array must be restored to it's original state before the subroutine is over.
  • You may not declare or use any other variables outside of what perl 'provides' to you. For example, you have to use @_ at some point to get the array, but perl provides this due to the nature of subroutines. You may also use variables like $_ when inside a map/grep/for block, or the use of $1, $2, etc in regex statements when these have been created for you. However, you may not use $_ or any other of the reserved perl variables outside of these types of blocks.
  • You cannot use an external module, nor a system command; the calculation should be done entire by perl itself and not call on any other programs.

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 (Initiate) 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

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://79981]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others surveying the Monastery: (4)
As of 2024-04-24 12:12 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found