I am trying to pass a function as an argument to a method of class. I know I need to use proc, but I am not sure I am using the right syntax. This is my attempt.
module MyApp;end
module MyApp::Stats
def self.sum(a)
a.inject(0){ |accum, i| accum + i }
end
def self.mean(a)
sum(a) / a.length.to_f
end
# Many more functions....
def self.sum_proc
Proc.new{|x| sum(x) }
end
def self.mean_proc
Proc.new{|x| mean(x)}
end
# And many more procs
end
class MyData
attr_reader :values
attr_reader :aggregates
def initialize(array)
#values = array
#aggregates = {}
end
def aggregate(aggregator)
puts aggregator.call(#values)
#I would also like to create a hash of aggregator. Something like:
#aggregates["aggregator"] = aggregator.call(#values)
end
end
I can then do
ar = [1,2,3,4,5,6,7,8,9]
data = MyData.new(ar)
And call the aggregate method in various ways:
aggregator = Proc.new{|x| MyApp::Stats.sum(x)}
data.aggregate(aggregator)
data.aggregate(Proc.new{|x| MyApp::Stats.mean(x)} )
data.aggregate(Proc.new{|x| x.count{|y| y > 3.0} })
data.aggregate(MyApp::Stats.sum_proc)
data.aggregate(MyApp::Stats.mean_proc)
I have two issues with this code. First it seems redundant as I have to define the aggregator first and then the associated proc, e.g. sum and sum_proc. Second, I wonder how I could pass any of the standard enumerator methods without defining a proc for it: say count or first.
Finally, I would like to create a hash for the aggregators so that I could do:
puts data.aggregates["sum"]
puts data.aggregates["mean"]
Methods aren't objects in Ruby. You can't pass a method as an argument because you can only pass objects as arguments.
However, you can get a Method proxy object representing a method by calling the method method and passing the name of the method as an argument. Method proxy objects duck-type Proc, so they respond to arity, parameters, and most importantly to_proc and call.
The idiomatic way of taking a single first-class procedure as an argument, is to take a block like this:
def aggregate
yield #values
end
and pass a method using the unary prefix & operator:
data.aggregate(&MyApp::Stats.:sum)
I dont know how to set array capacity so my array can store only 3 elements. E.g. if I try to push 4th element, it returns error.
Any ideas?
The default Array class doesn't have that functionality. So, your options are:
Create a separate class that stores the three elements and implement your own methods like push and [].
Subclass Array and override the methods to only allow for three elements, for example:
class ThreeElements < Array
def push(*stuff)
raise 'Already has three elements!' unless length < 3
super
end
end
In my opinion, #1 is the better option, because the default Array interface has too many methods to bother with overriding.
You can create a wrapper class for your array! You can also override the "[]" operator in and check if your index is valid:
def [](i)
# getter
end
def []=(i, v)
# setter
end
I am trying to write two instance methods, where method2 could process the output of method1.
For example, something like this:
puts Numbers::new(2,2).sum.sqrt
>16
I thought that the code would look something like this:
class Numbers
def initialize(x,y)
#x=x
#y=y
end
def sum
#z=#x+#y
end
def sqrt
#z**2
end
end
That is not the case and I get a NoMethodError when I try to call sqrt.
I know I am missing something easy and fundamental here, but I couldn't find a straight answer.
Thank you!
The sum method returns the value of #z which is a Fixnum. Ruby is trying to execute the sqrt method on the Fixnum instance and hence the exception.
May be this is what you want:
class Fixnum
def square
self**2
end
end
class Array
def sum
reduce(0, &:+)
end
end
Now you can:
[1,2].sum.square # 9
In order to call an instance method, you need an instance--your sum method needs to return the instance to allow chaining (same with sqrt).
Method chaining is common, but IMO a bit counter-intuitive in this case.
I'd like to add something like a callback function to a Ruby array, so that when elements are added to that array, this function is called.
One thing I can think of is to override all methods (like <<, =, insert, ...) and call that callback from there.
Is there an easier solution?
The following code only invokes the size_changed hook when the array size has changed and it is passed the new size of the array:
a = []
class << a
Array.instance_methods(false).each do |meth|
old = instance_method(meth)
define_method(meth) do |*args, &block|
old_size = size
old.bind(self).call(*args, &block)
size_changed(size) if old_size != size
end if meth != :size
end
end
def a.size_changed(a)
puts "size change to: #{a}"
end
a.push(:a) #=> size change to 1
a.push(:b) #=> size change to 2
a.length
a.sort!
a.delete(:a) #=> size change to 1
You should probably create your own class that wraps array. You don't want to override a core class with a callback like you are describing, not only does that make the code brittle but it becomes less expressive for future developers who may not be expecting Array to make a callback.
Use the "Observer" pattern to be notified of changes in the size of the array you wish to observer: Ruby Observer This saves you from having to override all methods that add an element to the array
I have an object Results that contains an array of result objects along with some cached statistics about the objects in the array. I'd like the Results object to be able to behave like an array. My first cut at this was to add methods like this
def <<(val)
#result_array << val
end
This feels very c-like and I know Ruby has better way.
I'd also like to be able to do this
Results.each do |result|
result.do_stuff
end
but am not sure what the each method is really doing under the hood.
Currently I simply return the underlying array via a method and call each on it which doesn't seem like the most-elegant solution.
Any help would be appreciated.
For the general case of implementing array-like methods, yes, you have to implement them yourself. Vava's answer shows one example of this. In the case you gave, though, what you really want to do is delegate the task of handling each (and maybe some other methods) to the contained array, and that can be automated.
require 'forwardable'
class Results
include Enumerable
extend Forwardable
def_delegators :#result_array, :each, :<<
end
This class will get all of Array's Enumerable behavior as well as the Array << operator and it will all go through the inner array.
Note, that when you switch your code from Array inheritance to this trick, your << methods would start to return not the object intself, like real Array's << did -- this can cost you declaring another variable everytime you use <<.
each just goes through array and call given block with each element, that is simple. Since inside the class you are using array as well, you can just redirect your each method to one from array, that is fast and easy to read/maintain.
class Result
include Enumerable
def initialize
#results_array = []
end
def <<(val)
#results_array << val
end
def each(&block)
#results_array.each(&block)
end
end
r = Result.new
r << 1
r << 2
r.each { |v|
p v
}
#print:
# 1
# 2
Note that I have mixed in Enumerable. That will give you a bunch of array methods like all?, map, etc. for free.
BTW with Ruby you can forget about inheritance. You don't need interface inheritance because duck-typing doesn't really care about actual type, and you don't need code inheritance because mixins are just better for that sort of things.
Your << method is perfectly fine and very Ruby like.
To make a class act like an array, without actually inheriting directly from Array, you can mix-in the Enumerable module and add a few methods.
Here's an example (including Chuck's excellent suggestion to use Forwardable):
# You have to require forwardable to use it
require "forwardable"
class MyArray
include Enumerable
extend Forwardable
def initialize
#values = []
end
# Map some of the common array methods to our internal array
def_delegators :#values, :<<, :[], :[]=, :last
# I want a custom method "add" available for adding values to our internal array
def_delegator :#values, :<<, :add
# You don't need to specify the block variable, yield knows to use a block if passed one
def each
# "each" is the base method called by all the iterators so you only have to define it
#values.each do |value|
# change or manipulate the values in your value array inside this block
yield value
end
end
end
m = MyArray.new
m << "fudge"
m << "icecream"
m.add("cake")
# Notice I didn't create an each_with_index method but since
# I included Enumerable it knows how and uses the proper data.
m.each_with_index{|value, index| puts "m[#{index}] = #{value}"}
puts "What about some nice cabbage?"
m[0] = "cabbage"
puts "m[0] = #{m[0]}"
puts "No! I meant in addition to fudge"
m[0] = "fudge"
m << "cabbage"
puts "m.first = #{m.first}"
puts "m.last = #{m.last}"
Which outputs:
m[0] = fudge
m[1] = icecream
m[2] = cake
What about some nice cabbage?
m[0] = cabbage
No! I meant in addition to fudge
m.first = fudge
m.last = cabbage
This feels very c-like and I know Ruby
has better way.
If you want an object to 'feel' like an array, than overriding << is a good idea and very 'Ruby'-ish.
but am not sure what the each method
is really doing under the hood.
The each method for Array just loops through all the elements (using a for loop, I think). If you want to add your own each method (which is also very 'Ruby'-ish), you could do something like this:
def each
0.upto(#result_array.length - 1) do |x|
yield #result_array[x]
end
end
If you create a class Results that inherit from Array, you will inherit all the functionality.
You can then supplement the methods that need change by redefining them, and you can call super for the old functionality.
For example:
class Results < Array
# Additional functionality
def best
find {|result| result.is_really_good? }
end
# Array functionality that needs change
def compact
delete(ininteresting_result)
super
end
end
Alternatively, you can use the builtin library forwardable. This is particularly useful if you can't inherit from Array because you need to inherit from another class:
require 'forwardable'
class Results
extend Forwardable
def_delegator :#result_array, :<<, :each, :concat # etc...
def best
#result_array.find {|result| result.is_really_good? }
end
# Array functionality that needs change
def compact
#result_array.delete(ininteresting_result)
#result_array.compact
self
end
end
In both of these forms, you can use it as you want:
r = Results.new
r << some_result
r.each do |result|
# ...
end
r.compact
puts "Best result: #{r.best}"
Not sure I'm adding anything new, but decided to show a very short code that I wish I could have found in the answers to quickly show available options. Here it is without the enumerator that #shelvacu talks about.
class Test
def initialize
#data = [1,2,3,4,5,6,7,8,9,0,11,12,12,13,14,15,16,172,28,38]
end
# approach 1
def each_y
#data.each{ |x| yield(x) }
end
#approach 2
def each_b(&block)
#data.each(&block)
end
end
Lets check performance:
require 'benchmark'
test = Test.new
n=1000*1000*100
Benchmark.bm do |b|
b.report { 1000000.times{ test.each_y{|x| #foo=x} } }
b.report { 1000000.times{ test.each_b{|x| #foo=x} } }
end
Here's the result:
user system total real
1.660000 0.000000 1.660000 ( 1.669462)
1.830000 0.000000 1.830000 ( 1.831754)
This means yield is marginally faster than &block what we already know btw.
UPDATE: This is IMO the best way to create an each method which also takes care of returning an enumerator
class Test
def each
if block_given?
#data.each{|x| yield(x)}
else
return #data.each
end
end
end
If you really do want to make your own #each method, and assuming you don't want to forward, you should return an Enumerator if no block is given
class MyArrayLikeClass
include Enumerable
def each(&block)
return enum_for(__method__) if block.nil?
#arr.each do |ob|
block.call(ob)
end
end
end
This will return an Enumerable object if no block is given, allowing Enumerable method chaining