From: Fearless Fool on
Assume I'd like to create a statistics package designed to work on
Arrays [but see P.S.]. One way to do this would be to create functions
that take an array as an argument:

def mean(array)
return NaN unless array && array.size > 0
array.reduce(0.0) {|accum, val| accum + val} / array.size
end

... but that's so old school. What I'd really like is to *cleanly*
extend the Array class so you can operate on it directly, as in:

>> [1, 2, 3].mean
=> 2.0

I'm sure that it's considered bad style to simply open the Array class
and extend it, since that can lead to unexpected user surprises:

class Array
def mean
return NaN unless self.size > 0
self.reduce(0.0) {|accum, val| accum + val} / self.size
end
end

My hunch is that the stats functions should be wrapped in a module and
then included (extended?) when desired. But despite reading the
tutorials and docs, I'm not sure what's considered the stylistically
correct way (and the syntax) to package that up.

Comments? Pointers?

Thanks!

- ff

[P.S.: Before you tell me about available stats packages, etc, note that
I'm only using statistics as an example. My real question is about
extending a class...]
--
Posted via http://www.ruby-forum.com/.

From: David Masover on
On Monday, July 05, 2010 03:30:44 pm Fearless Fool wrote:
> Assume I'd like to create a statistics package designed to work on
> Arrays [but see P.S.]. One way to do this would be to create functions
> that take an array as an argument:
>
> def mean(array)
> return NaN unless array && array.size > 0
> array.reduce(0.0) {|accum, val| accum + val} / array.size
> end
>
> ... but that's so old school. What I'd really like is to *cleanly*
>
> extend the Array class so you can operate on it directly, as in:
> >> [1, 2, 3].mean
>
> => 2.0
>
> I'm sure that it's considered bad style to simply open the Array class
> and extend it, since that can lead to unexpected user surprises:
>
> class Array
> def mean
> return NaN unless self.size > 0
> self.reduce(0.0) {|accum, val| accum + val} / self.size
> end
> end

What kind of surprises?

> My hunch is that the stats functions should be wrapped in a module and
> then included (extended?) when desired. But despite reading the
> tutorials and docs, I'm not sure what's considered the stylistically
> correct way (and the syntax) to package that up.

Simple enough:

module MyModule
def mean
...
end
end

Then, you can always do this:

[].extend MyModule

Or this:

class Array
include MyModule
end

Then, probably, provide a file people can 'require' that'll do the inclusion
for them, maybe even make that the default.

It might also be nice to provide a way to do this on Enumerables, though that
would be less efficient if you know you have an array. For example:

def mean
size = 0
count = inject(0){|accum, val| size+=1; accum+val}
count / size.to_f
end

From: Jesús Gabriel y Galán on
On Mon, Jul 5, 2010 at 11:22 PM, David Masover <ninja(a)slaphack.com> wrote:
> On Monday, July 05, 2010 03:30:44 pm Fearless Fool wrote:
>> Assume I'd like to create a statistics package designed to work on
>> Arrays [but see P.S.].  One way to do this would be to create functions
>> that take an array as an argument:
>>
>>   def mean(array)
>>     return NaN unless array && array.size > 0
>>     array.reduce(0.0) {|accum, val| accum + val} / array.size
>>   end
>>
>> ... but that's so old school.  What I'd really like is to *cleanly*
>>
>> extend the Array class so you can operate on it directly, as in:
>>   >> [1, 2, 3].mean
>>
>>   => 2.0
>>
>> I'm sure that it's considered bad style to simply open the Array class
>> and extend it, since that can lead to unexpected user surprises:
>>
>>   class Array
>>     def mean
>>       return NaN unless self.size > 0
>>       self.reduce(0.0) {|accum, val| accum + val} / self.size
>>     end
>>   end
>
> What kind of surprises?
>
>> My hunch is that the stats functions should be wrapped in a module and
>> then included (extended?) when desired.  But despite reading the
>> tutorials and docs, I'm not sure what's considered the stylistically
>> correct way (and the syntax) to package that up.
>
> Simple enough:
>
> module MyModule
>  def mean
>    ...
>  end
> end
>
> Then, you can always do this:
>
> [].extend MyModule

I agree, this is the less dangerous thing to do: just extend the
specific array instances with your functionality. That way, all other
arrays are left intact.

Jesus.