describe Array do
describe "#sum" do
it "has a #sum method" do
expect([]).to respond_to(:sum)
end
it "should be 0 for an empty array" do
expect([].sum).to eq(0)
end
it "should add all of the elements" do
expect([1,2,4].sum).to eq(7)
end
end
end
the code above is the test code given to me. and I made the code below to be tested by the test code.
class Array
def initialize(arr)
#arr = arr
end
def sum
if #arr == nil
return 0
else
numArr = #arr
total = 0
numArr.each {|num|
total += num
}
return total
end
end
end
I thought it would return total 7. (1+2+4 = 7) but it returns 0... I guess it doesn't take [1,2,4] array as a parameter. what am I doing wrong?
As the existing array class already has an initializer, and self inside the instance of an array already is an array, you don't need to add your own initializer
class Array
def sum
self.inject(0){|sum,x| sum + x }
end
end
should do what you want. Lookup inject if it isn't clear, but it does essentially the same that you tried to do with your code, just that you were using a local variable to store the sum.
If this is not only an experiment however I recommend not do monkey patches on core classes if avoidable (and it usually is avoidable). Here's an article with some hints how to do it a bit cleaner, if not avoidable: http://www.justinweiss.com/articles/3-ways-to-monkey-patch-without-making-a-mess/
Related
I'm reading the "Pickaxe" ruby book and I came across this example:
def meth_three
100.times do |num|
square = num*num
return num, square if square > 1000
end
end
If you call meth_three in irb it returns the first integer between 1 and 100 that has a square > 1000:
meth_three # => [32, 1024]
My question is, how does the times method know how to loop through each integer between 1..100 to pass as the argument to the |num| parameter?
There are many, many different ways of writing iteration in Ruby.
Here is one possible way that it could be implemented recursively:
class Integer
def times(&blk)
return enum_for(__callee__) unless block_given?
return self unless positive?
pred.times(&blk)
yield pred
end
end
Or, using Range#each:
class Integer
def times(&blk)
return enum_for(__callee__) unless block_given?
return self unless positive?
(0...self).each(&blk)
self
end
end
Or, with a loop:
class Integer
def times
return enum_for(__callee__) unless block_given?
return self unless positive?
i = -1
yield i while (i += 1) < self
self
end
end
And a tail-recursive implementation just for the fun of it:
class Integer
def times(&blk)
return enum_for(__callee__) unless block_given?
return self unless positive?
__times_rec(0, &blk)
self
end
private def __times_rec(i, &blk)
return unless i < self
yield i
__times_rec(i.succ, &blk)
end
end
Here is the actual code from an actual Ruby implementation (core/integer.rb from Rubinius):
def times
return to_enum(:times) { self } unless block_given?
i = 0
while i < self
yield i
i += 1
end
self
end
TruffleRuby uses the exact same code as well (see src/main/ruby/truffleruby/core/integer.rb), as does JRuby (see core/src/main/ruby/jruby/ruby_implementations/Integer/times.rb)
Here is another real example from a real Ruby implementation (opal/corelib/number.rb from Opal):
def times(&block)
return enum_for(:times) { self } unless block
%x{
for (var i = 0; i < self; i++) {
block(i);
}
}
self
end
As you can see, there are many ways in which one could write a loop in Ruby.
Some of the comments hint at what is going on. I think it can be explained like this:
Each integer in Ruby is actually an object, more specifically an instance of the Integer class.
You can think of it as a Integer#times method, that could naively be implemented something like this:
class Integer
def times(&block)
if self.value > 0
for i in 0...self.value do
block.call(i)
end
end
end
end
This method is actually implemented in C in the official version of ruby (MRI), as pointed out in one of the comments to your question. I only write something similar here to help explain the concept on how it could have looked in Ruby.
The times method actually returns a value, which is the current iterator with every loop it makes (1, 2, 3 ... 100).
The do keyword catches this iterator, and uses it as the variable num.
Anytime you see the num variable, you are seeing the current iterator that's being returned by times.
I am working on building the flatten method in Ruby. Here is my code:
require 'byebug'
class Array
##new_array = []
def new_flatten
self.each do |element|
if !element.is_a? Array
##new_array << element
elsif element.is_a? Array
element.new_flatten
end
end
##new_array
end
end
test_array = ([1,2,3,[4,5,6,[7]]])
puts test_array.new_flatten == test_array.flatten
My question is this. Do I need to use the class level variable? I would like to use an instance variable but it doesn't seem to get called when I do:
test_array = []
or
Array.new([])
So I had to create a class-level variable to hold the new flattened array.
The problem with using a class variable is that the same one is visible to all the instances, and new_flatten is an operation for an instance. You should refactor your new_flatten algorithm so it doesn't rely on what is, in effect, a "global" variable for the method calls.
For example, you can use a local variable and Ruby's facility to append arrays with +:
class Array
def new_flatten
a = []
self.each do |element|
if element.is_a? Array
a += element.new_flatten
else
a << element
end
end
a
end
end
Also, as some tweaks of style, when doing an if-else, it's often clearer to state the positive logic first. And, you don't need the elsif in this case since the else is the complement of the if. So rather than:
if not something
do_stuff
elsif opposite_of_not_something
do_other_stuff
end
It's clearer to say:
if something
do_stuff
else
do_other_stuff
end
Your question is really confusing. In your title, you talk about calling initialize, but there is no initialize in your code. In your question, you talk about calling an instance variable, but you can't call variables, only methods.
However, implementing flatten is just a simple fold, I don't see the need for storing any intermediate state:
class Array
def new_flatten
inject([]) {|acc, el| acc + Array(case el when Array then el.new_flatten else el end) }
end
end
I came across this code:
class RandomSequence
def initialize(limit,num)
#limit,#num = limit,num
end
def each
#num.times { yield (rand * #limit).floor }
end
end
i = -1
RandomSequence.new(10,4).each do |num|
i = num if i < num
end
Is it the case that the each method is called only once and will compute four different values, and then for each of those values we execute code block between do and end? Is my understanding of control flow correct?
Your understanding is close. The random number will be generated, then a block yielded, then another generated, etc 4 times. You can verify this easily by adding puts statements into you blocks to see when they are executed.
class RandomSequence
def initialize(limit,num)
#limit,#num = limit,num
end
def each
puts "in each"
#num.times { yield (rand.tap {|x| puts "Generated #{x}" } * #limit).floor }
end
end
i = -1
RandomSequence.new(10,4).each do |num|
puts "in block"
i = num if i < num
end
Outputs
in each
Generated 0.6724385316643955
in block
Generated 0.8906983274750662
in block
Generated 0.49038868732214036
in block
Generated 0.38100454011243456
in block
The each method on the RandomSequence class is called once. In it #num.times which creates an Enumerator. The Enumerator is iterated over and the block with the yield statement is called (with the argument to it ignored).
The yield statement calls the block that's passed to the each method passing the value of (rand * #limit).floor. In your code the block is not bound to a variable, i.e you could get a reference to the block by doing:
def each(&block)
#... do stuff with block, e.g. block.call("some args")
end
which can be useful at times.
A bit off topic, but one thing I found scary with Ruby starting out is that a return statement returns the flow of execution from where it was defined.
def create_proc
puts "Creating proc"
Proc.new do
puts "In proc!"
return "some value" # notice the explicit return
end
end
def do_stuff
my_proc = create_proc
my_proc.call # This will cause a runtime error
end
If the explicit return is removed everything works there is no error... Lesson being that in ruby you should probably avoid using explicit returns.
I defined my own method to access elements as:
class Array2
def [](key)
if key.kind_of?(Integer)
#elements[key]
else
# ...
end
end
end
If I had previously declared #elements as Array.new, both the operations:
list = Array2.new
# ...
puts list[0]
puts list.[](0)
work properly. Why is the first operation acceptable?
Both the list[0] and list.[](0) syntaxes mean the exact same thing. They call the [] method with an argument 0 on the list object.
I have the following situation. I am trying to write a unit test for an array of objects. The object is defined something like this:
class Element
attr_reader :title, :season, :episode
def initialize ( name, number )
#name = name
#number = number
end
def to_s
number = "%02d" % #number
result = "Number " << number << " " << #name
result
end
end
During the test I assert two arrays which both contain three elements, the elements are identical and even the order is identical still I get an error that the assert isn't equal. I guess I am missing something really basic here, whats the catch?
If I compare each element by the to_s method, the assert is correct.. Is that the way it should be done in the first place?
Try declaring a method == for your class, with the following code.
def ==(other)
self.to_s == other.to_s
end
Sidenote, you might want to refactor your to_s method too, for some concise code.
def to_s
"Number %02d #{#name}" % #number
end
Edit:
Numbers already have an == method defined (https://github.com/evanphx/rubinius/blob/master/kernel/bootstrap/fixnum.rb#L117).
Ruby compares arrays by running an == compare on each element of an Array. Here's the implementation of == on Arrays, as done in Rubinius (a Ruby implementation written almost completely in Ruby itself) https://github.com/evanphx/rubinius/blob/master/kernel/common/array.rb#L474.
If you leave out various error detections, it basically runs an == on all the elements of the array, recursively, and returns true if all of them match.