I have:
class Thing
def initialize
#array = [[0, 0, 0], [1, 1, 1]]
end
end
thing = Thing.new
The normal way to access an element in #array is to use [] as in:
#array[0][1] # => 0
I am trying to overwrite [] so as to get results like this:
position_array = [0, 1]
#array[position_array] # => 0
This is my attempt:
class Thing
def [](position_array)
index_row, index_col = position_array
#array[index_row][index_col]
end
def get_value(position_array)
#array[position_array] # doesn't work
# self[position_array] # does work
end
end
thing.get_value([0, 1])
# >> 'get_value': no implicit conversion of Array into Integer (TypeError)
Why do I need to index the Thing object in order to index #array?
Just think of message and receiver.
#array[position_array] sends the message [] to the receiver #array. #array is an instance of Array, so the method Array#[] gets invoked.
self[position_array] sends the message [] to the receiver self. Within instance methods, self refers to that instance. And because self is an instance of Thing, the method Thing#[] gets invoked.
Since Thing is a subclass of Object and not a subclass of Array (nothing wrong here, you shouldn't subclass Array anyway), your implementation of [] does not override Array#[]. Both methods are totally independent of each other, just like String#[] or Hash#[].
This is how I would approach it:
class Thing
def initialize
#array = [[1, 2, 3], [4, 5, 6]]
end
def [](i, j)
#array[i][j]
end
end
thing = Thing.new
thing[0, 1] #=> 2
thing[1, 1] #=> 5
You could use a prepended method to non-invasively override the [] method in Array by duck-typing the parameter passed to the [] method, and then calling the original if its not what you expect. Then you don't need a Thing object at all.
module MyArrayExtension
def [] (*param)
if param.size == 2
row, col = param
raise ArgumentError, 'Row must be an integer' if row.class != Integer
raise ArgumentError, 'Column must be an integer' if col.class != Integer
raise ArgumentError, "Element at row #{row} is not an array" if self[row].class != Array
self[row][col]
else
super
end
end
end
class Array
prepend MyArrayExtension
end
thing = [[1,2,3],[4,5,6]]
puts "The 2D array is: #{thing}"
puts "Extension used on the thing to get at element 1 of first array:"
puts thing[0,1]
puts '-' * 20
normal = [1,2,:blah,4,5]
puts "Normal array is #{normal}"
puts "Original [] method used to get the 3rd element:"
puts normal[2]
puts '-' * 20
puts "Using the extension on the non-2D array:"
puts normal[0,1]
The output of this program is:
The 2D array is: [[1, 2, 3], [4, 5, 6]]
Extension used on the thing to get at element 1 of first array:
2
--------------------
Normal array is [1, 2, :blah, 4, 5]
Original [] method used to get the 3rd element:
blah
--------------------
Using the extension on the non-2D array:
./test.rb:9:in `[]': Element at row 0 is not an array (ArgumentError)
from ./test.rb:35:in `<main>'
Related
I need help understanding inheritance.
class MyArray < Array
end
a = MyArray[1, 2, 3] #=> [1, 2, 3]
b = MyArray[4, 5] #=> [4, 5]
c = a + b #=> [1, 2, 3, 4, 5]
a.class #=> MyArray
b.class #=> MyArray
c.class #=> Array
I don't understand why the result of the addition is not an instance of MyArray class.
I don't understand why my array "a" is not "MyArray" class after add.
Why should it (be a MyArray)? Concatenation operation on arrays is defined to return a new Array, so that's what happens here. https://ruby-doc.org/core-2.5.3/Array.html#method-i-2B
If you want, you can override that operation in your class to return an instance of MyArray. Don't forget about all other similar methods.
This is also why it's a bad idea to subclass standard collections. Better to use composition over inheritance here.
Just to add a bit to Sergio's answer in terms of his comment on using composition over inheritance and the exchange in the comments.
Instead of saying MyArray is an array you can say MyArrayLike has and array.
Then you can "forward" methods that make sense to the underlying array but still add your own functionality that makes sense for your class without sub-classing array.
Ruby even has several ways to make this very easy including the Forwardable module.
class MyArrayLike
attr_reader :arr
def initialize( initial_arr )
#arr = initial_arr
end
def +(other)
result = self.class.new(arr + other.arr)
# maybe you want to do more than just concat the underlying array, if so you can do it here
result
end
def first
# for example maybe you want first to just return the first item in the underlying array.
arr.first
end
end
a = MyArrayLike.new([1,2,3])
b = MyArrayLike.new([4,5])
puts "a.class = #{a.class}"
# => a.class = MyArrayLike
puts a
# => #<MyArrayLike:0x00000000dc4b00>
a += b
puts "a.class = #{a.class}"
# => a.class = MyArrayLike
puts a
# => #<MyArrayLike:0x00000000dc4470>
puts a.first
# => 1
puts a.arr
# => 1
# 2
# 3
# 4
# 5
Adding MyArray to MyArray to get Array may be counterintuitive, but a method can be defined to return any class. And in the case of Array#+, which you are calling, it just happens to be defined to return an Array. That's it.
If you want it to return a MyArray, one way to do it is to define MyArray#+ as follows:
class MyArray < Array
def +other
MyArray.new(super)
end
end
(MyArray.new([1, 2, 3]) + MyArray.new([4, 5])).class # => MyArray
By the way, notice that your MyArray#initialize definition is meaningless, and hence redundant.
I am implementing the each method on my own. I am supposed to explicitly return self before closing the method. This is my code:
module Enumerable
def my_each
for i in self
yield i
end
#self
end
end
[1,2,3,4].my_each {|x| x + 1} # => [1,2,3,4]
Why does the code still return the receiver even though I did not explicitly return self on the last line?
why does the code still return self even though I did not explicitly return self on the last line?
If you don't specify a return value explicitly, a method will return the last expression evaluated. The last expression in your method is the for loop.
From its documentation:
The result value of a for loop is the value iterated over unless break is used.
Examples:
for i in 1..10
end
#=> 1..10
for i in [1, 2, 3]
end
#=> [1, 2, 3]
for i in [1, 2, 3]
break :foo
end
#=> :foo
Ok, reviewing Procs, lambdas, and blocks via this link.
Question on this code:
class Array
def iterate!
self.each_with_index do |n, i|
self[i] = yield(n)
end
end
end
array = [1, 2, 3, 4]
array.iterate! do |n|
n ** 2
end
puts array.inspect
Conceptually, I understand almost everything, except one line which is this:
self[i] = yield(n)
I get that this self in this line self.each_with_index do |n, i| means that it's a class method, right?
But why do we need to assign the parameters in yield(n) to self[i]?
Please explain in super basic way if you can.
(in other words, please be nice - which people generally are for most part here - just a little extra nervous that I'm not getting this which is making me feel stupid)
The method is iterate!, which is an instance method. self in self.each_with_index is the receiver of the method Enumerable#each_with_instance. Since self is the current instance of Array ([1,2,3,4] in your example), self. is not needed; i.e., you could (and imo, should) just write each_with_index do |n, i|.... In other words, self is the implied receiver when no explicit receiver is specified.
Regarding the line:
self[i] = yield(n)
for your example array = [1,2,3,4] your enumerator is:
enum = [1,2,3,4].each_with_index
#=> #<Enumerator: [1, 2, 3, 4]:each_with_index>
with elements
enum.to_a
#=> [[1, 0], [2, 1], [3, 2], [4, 3]]
The first element passed into block by Array#each is therefore [1,0], which is assigned to the block variables:
n = 1
i = 0
resulting in
self[0] = yield(1) => 1**2 => 1
and so on.
I'll try to explain in a super basic way.
I get that this self in this line self.each_with_index do |n, i| means
that it's a class method, right?
Nope. The meaning of self depends on the context. If self was in the class, it would refer to the class. But here self is in an instance method, so it refers to the instance (so each_with_index is also an instance method).
But why do we need to assign the parameters in yield(n) to self[i]?
The goal of iterate! is to modify the array in place. Since self refers to the instance, self[i] accesses the elements of the array that iterate! is being called on, thus modifying the array in place.
Also, I'm not sure what you mean by "parameters" here. yield(n) passes n to the block, runs the block, and returns the value.
self[i] = yield(n) reassigns the values in the array, to the block that was specified in
array.iterate! do |n|
n ** 2
end
which basically means, take the value of the array, and square it, save that value in the element of the array. So [1, 2, 3 , 4] becomes [1 ** 2, 2 ** 2, 3 ** 2, 4 ** 2] => [2, 4, 9, 16]
Self changes with(and actually is) the current context or surrounding object.
Since
self.each_with_index do |n, i|
...
is monkey patching the Array class and is within an instance method iterate!, self refers to the instance itself: in this case the array [1, 2, 3, 4].
You're probably thinking of this:
class some_class
def self.a_class_method
...
which is defined in the context of a class. So self is the class itself(which is also an object), not an instance of that class.
Since self is just the array [1, 2, 3, 4]
self[i] = yield(n)
is replacing each element of the array with results of the sent in block.
Here iterate! is an instance function of Array class and you have an array object.When you do
array.iterate! do |n|
n ** 2
end
You are passing a block 'do |n| n**2 end' to iterate! function.In the function you can access this block using yield.But as you can see block is expecting one parameter through |n| so you need to pass one parameter and the block code will return the square of it.
self[i] = yield(n)
self is being used in Array instance context.So it is modifying the values of array.
For more information please check this article:
http://geekdirt.com/blog/blocks-lambda-and-procs-in-ruby/
I'm doing http://www.rubeque.com/problems/queue-continuum/solutions/51a26923ba804b00020000df and I spent a while there. I can't realize why this code doesn't pass
def initialize(queue)
#q = queue
end
def pop(n=1)
#q.shift(n)
end
def push(arr)
arr.each { |x|
#q.push(x)
}
return true
end
def to_a
#q
end
but this works perfectly.
def initialize(queue)
#q = queue
end
def pop(*n)
#q.shift(*n)
end
def push(arr)
#q.push(*arr)
return true
end
def to_a
#q
end
i'm totally confused about
def pop(*n)
#q.shift(*n)
end
and
def push(arr)
#q.push(*arr)
end
why should I take (arr) as array and than change it into... *arr which is Array of array? I'm confused, please help!
The splat works in two ways.
When receiving arguments, it combines arguments into an array.
def foo *args; args end
foo(1) # => [1]
foo(1, 2, 3) # => [1, 2, 3]
When giving arguments, it decomposes an array into arguments.
def bar x, y, z; y end
bar(*[1, 2, 3]) # => 2
def baz x; x end
baz(1) # => [1]
baz(1, 2, 3) # => Error
The *arr you are wondering is the latter case. It is not an object like [1, 2, 3] (hence, not an array of arrays). It is a part of arguments (like 1, 2, 3) passed to a method.
There are other uses of splats (as in array literals, case statements, etc.), but their function is either of the two uses above.
I have a class Test:
class Test
attr_accessor :data
def initialize
#data = [0, 1, 2, 3]
end
def map
#data.map!{|i| i = yield i }
end
end
and I attempt to use it like:
a = Test.new
a.map{|i|
if(i==2)
i+=1
break i #<--- -This line is the focus
else
1
end
}
puts a.data
The output I expect is [1, 1, 3, 3]. Instead, I get [1, 1, 2, 3]. The last iteration of the block in map doesn't return the modified i.
I replaced break i with next i. This performed as I expected, and produced the output [1, 1, 3, 1].
How can I modify this piece of code (or, ideally the line I point out in my second code-snippet) so that I would get the output [1, 1, 3, 3]? In other words, how can I make the block finish, but pass one last value back to map? Is there a neat and readable way to do this (besides, say, toggling a boolean flag break_now)?
I'm assuming you're asking how to leave a block and make use of the last value that was calculated rather than how to calculate a specific set of numbers; for the latter, there is probably a clever one-liner.
How about something like this:
class Test
attr_accessor :data
def initialize
#data = [0, 1, 2, 3]
end
def modify
#data.map! {|i| yield i }
end
end
a = Test.new
a.modify do |i|
break i if #done
#done = i == 2
#done ? (i + 1) : 1
end
puts a.data
An additional thought—#map is an important method in Ruby with a specific interface. In your example you're violating the interface by modifying a field in Test. For this reason I've used the name #modify instead.
In general, you could get away with this by modifying the yielded values in place. For example, if your array consisted of Strings instead of Fixnums:
class Test
attr_accessor :data
def initialize
#data = %w{a b c d}
end
def map
#data.map! { |i| yield i }
end
end
a = Test.new
a.map do |i|
if i == 'c'
i.next!
break
else
'b'
end
end
p a.data #=> ["b", "b", "d", "d"]
The problem with your example is this:
Fixnum objects have immediate value. This means that when they are assigned or passed as parameters, the actual object is passed, rather than a reference to that object. Assignment does not alias Fixnum objects. There is effectively only one Fixnum object instance for any given integer value…
Fixnums can't be altered in-place, so your expression i += 1 in the lower block doesn't affect the value of i in the upper block. That's why you get 2 in your example but d in my example.
You have to do this:
a.map{ |i| (i % 2 == 0) ? i + 1 : i }
When you use map function you don't change 'a' variable, if you want change 'a' variable do this:
a.map!{ |i| (i % 2 == 0) ? i + 1 : i }
The new value of 'i' is the value return by the block, so don't do something like:
a.map{|i| i = 1 }
because if you do:
a.map{|i| i = 1; 5 }
the result will be:
[5, 5, 5, 5]