The spec:
# spec/row_spec.rb
require "spec_helper"
module Mastermind
describe Row do
context "#initialize" do
it "has four elements by default" do
row = Row.new
expect(row.values.size).to eq 4
end
it "can only go up to 6" do
row = Row.new
expect(row.values.max).to be <= 6
end
end
end
end
The code:
# lib/mastermind/row.rb
module Mastermind
class Row
attr_accessor :values
def initialize (values=random_row)
#values = values
end
def random_row
4.times {random||=[] << rand(1..6)}
end
end
end
Row is supposed to be a row for a game of mastermind, with four random values between 1 and 6, and this test is supposed to make sure it is functioning properly. It looks like values is returning an int instead of an array. Why?
How would I troubleshoot this on my own? I tried to make something simple to see exactly what was happening with values by appending
row = Row.new
puts row.values
to my row.rb, but I just get an uninitialized constant error. Why doesn't this work?
This is your problem (random_row):
[1] pry(main)> 4.times {random||=[] << rand(1..6)}
=> 4
times returns the value. You want something like this:
[2] pry(main)> 4.times.map { rand(1..6) }
=> [6, 5, 3, 6]
Your other issue is because you didn't specify the module, Row doesn't exist, Mastermind::Row does.
[4] pry(main)> row = Mastermind::Row.new
=> #<Mastermind::Row:0x00000101c593e0 #values=4>
[5] pry(main)> row.values
=> 4
Your issue is that #random_row will return 4 (the return value of Fixnum#times is self), which means that when you initialize the Row, #values is set to 4.
You probably want something like:
def random_row
Array.new(4) { rand(1..6) }
end
Related
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>'
I created a sample ruby program to swap elements in an array
class Swap
def swp(a,b)
self[a],self[b] = self[b],self[a]
self
end
array = [1,2,3]
array.swp(1,2)
puts array
end
I am getting the following error
NoMethodError: private method `swp' called for [1, 2, 3]:Array
from (irb):77:in `<class:Swap>'
from (irb):71
from /home/rahulv/.rvm/gems/ruby-2.0.0-p481/gems/railties-3.2.16/lib/rails/commands/console.rb:47:in `start'
from /home/rahulv/.rvm/gems/ruby-2.0.0-p481/gems/railties-3.2.16/lib/rails/commands/console.rb:8:in `start'
from /home/rahulv/.rvm/gems/ruby-2.0.0-p481/gems/railties-3.2.16/lib/rails/commands.rb:41:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'
Please help to fix it.
UPDATE
I tried the same program with dynamic array input like this:
class Swap
def swp(a,b)
self[a],self[b] = self[b],self[a]
self
end
end
puts "enter the array elements "
input = gets.chomp
ary = []
while input != 'fin'
ary << input.to_i
input = gets.chomp
end
puts ary
puts "enter the elements to be swapped"
a=gets
b=gets
s=Swap.new
ary = ary.s.swp(a,b)
puts ary
Now the output error is like this:
enter the array elements
1
2
3
4
5
fin
enter the positions to be swapped
1
2
NoMethodError: private method `s' called for [0, 1, 2, 3, 4, 5]:Array
from (irb):17:in `<class:Swap>'
from (irb):1
from /home/rahulv/.rvm/gems/ruby-2.0.0-p481/gems/railties-3.2.16/lib/rails/commands/console.rb:47:in `start'
array.class = Array, you should add swp method for Array class.
class Array
def swp(a,b)
self[a],self[b] = self[b],self[a]
self
end
end
array = [1,2,3]
array.swp(0,1) #The array index start from 0, not 1
puts array.inspect
=> [2, 1, 3]
Update:
class Array
def swp(a,b)
self[a],self[b] = self[b],self[a]
self
end
end
puts "enter the array elements "
ary = []
input = 0
while 1
input = gets.chomp
if input != 'fin'
ary << input.to_i
else
break
end
end
puts "enter the positions to be swapped"
a=gets.to_i
b=gets.to_i
ary.swp(a,b)
puts ary.inspect
I don't like editing classes like that, for example if there's another class in the ancestor tree that implements the actual swp method (for any reason) and you implemented yours, and you try to use the actual documented one, you'll find unexplained behaviour, and it might be uneasy to figure out why it's behaving like that, beacuse your method is hiding, it's more maintainable if you add your methods to the ancestry tree using a module and then including it to the class.
module Swappable
def swp(a, b)
# method goes here
end
end
Then in another file you would do
class Array
include Swappable
end
This way when you call Array.ancestors you'd see your module
Array.ancestors
=> [Array, Swappable, ....]
And when you try something like
Array.instance_method(:swp).owner
you'll get Swappable, while an open class editing would return Array
This is just my oppinion on this matter, you can pick whatever works for you.
You can try the following code blog for take input an array and swap with two positions.
Here I initialize Swap class with an input array and then pass two swap position.
Main Class
class Swap
def initialize(array)
#array = array#[1,2,3,4,5]
end
def swp(a,b)
#array[a],#array[b] = #array[b],#array[a]
#array
end
end
Take Input for array
puts "ArrayInput :: Enter numbers separated by spaces, press 'Enter' to finish:"
array = gets
array = array.split(" ")
Take Input for swap positions
puts "SwapInput:: For FirstPosition, press 'Enter' to finish:"
first_position = gets.to_i
puts "SwapInput:: For SecondPosition, press 'Enter' to finish:"
second_position = gets.to_i
Initialize Swap Class
swap = Swap.new(array)
array = swap.swp(first_position, second_position)
puts "Array after swap:"
puts array
I'm going through a problem on Ruby Monk, https://rubymonk.com/learning/books/1-ruby-primer/problems/155-restaurant#solution4804
Their solution is great; I like it and it's more compact than mine. Problem is for mine, I just don't understand why it only works when I remove the splat operator from the cost parameter orders. Even if I shouldn't be doing it this way, I'm struggling to figure out what's up. I know sometimes it's unnecessary to understand everything, and it's best to just move on.. but curious.
Here is mine:
class Restaurant
def initialize(menu)
#menu = menu
end
def cost(*orders)
total_cost = 0
orders.each do |item, number|
total_cost += #menu[item] * number
end
end
menu = {:rice => 3, :noodles => 2}
orders = {:rice => 1, :noodles => 1}
eat = Restaurant.new(menu)
puts eat.cost(orders)
Edit:
To include their suggested solution below
class Restaurant
def initialize(menu)
#menu = menu
end
def cost(*orders)
orders.inject(0) do |total_cost, order|
total_cost + order.keys.inject(0) {|cost, key| cost + #menu[key]*order[key] }
end
end
end
Edit:
To clear up and answer my own question in the comment
I tried these experiments and it shows inject "removing" the array brackets that splat "put on". Perhaps not the most proper way to think about it? It does help clear up my confusion.
order = { :rice => 1, :noodles => 1 }
menu = { :rice => 3, :noodles => 2 }
[order].inject(0) do |bla, blu|
p bla #=> 0
p blu #=> {:rice=>1, :noodles=>1}
p blu.keys #=> [:rice, :noodles]
end
When you write:
def cost(*orders)
end
then all the parameters passed to the cost method will be put into a single array named orders. These two are thus equivalent:
def cost(*orders)
p orders.class #=> Array
p orders #=> [1,2,3]
end
cost(1,2,3)
def cost(orders)
p orders.class #=> Array
p orders #=> [1,2,3]
end
cost( [1,2,3] ) # note the array literal brackets
In your case, when you remove the "splat" you are saying "set orders to reference whatever was passed in directly". In this case you're passing it a Hash, and when you iterate a hash you get key/value pairs for each entry. This is just what you want.
When you do have the splat, though, you're getting this:
def cost(*orders)
p orders.class #=> Array
p orders #=> [{:rice=>1, :noodles=>1}]
end
orders = {:rice=>1, :noodles=>1}
cost(orders)
So you're wrapping your hash in an array, and then iterating over the elements of the array. Thus, the first value passed to the block is the entire hash, and there is no second parameter.
def cost(*orders)
p orders.class #=> Array
p orders #=> [{:rice=>1, :noodles=>1}]
orders.each do |item,number|
p item #=> {:rice=>1, :noodles=>1}
p number #=> nil
end
end
orders = {:rice=>1, :noodles=>1}
cost(orders)
At this point you can't multiply anything by nil and so your code breaks.
Please help to explain what is needed in my code to decipher if the array contains an integer, if it does I need to add 1 to it and if doesn't if will just display the string or symbol.
I have left #notes where my brain stopped working
# possible arrays
# array = [1, "two", :three]
# array = [1, 2, 3]
class Array
def new_map
result = []
self.each do |item|
yield(item)
if #check to see if item is an integer
result << item + 1
else
# add string to result array
end
end
result
end
end
Here is the Rspec test:
describe "Array" do
describe "new_map" do
it "should not call map" do
a = [1, 2, 3]
a.stub(:map) { '' }
a.new_map { |i| i + 1 }.should eq([2, 3, 4])
end
it "should map any object" do
a = [1, "two", :three]
a.new_map { |i| i.class }.should eq([Fixnum, String, Symbol])
end
end
end
if item.is_a? Integer
result << item + 1
class Array
def new_map
result = []
self.each do |item|
yield(item)
if item.class == Integer # or if item.is_a? Integer
result << item + 1
else
# add string to result array
end
end
result
end
end
example:
=> 1.is_a? Integer
=> true
=> "1".is_a? Integer
=> false
=> 1_000_000.is_a? Integer
=> true
=> 1_000_000.class
=> Fixnum
=> 1_000_000.is_a? Integer
=> true
Try this:
class Array
def new_map
map do |item|
yield(item)
end
end
end
Actually you first spec does not make sense. You yield i + 1 to the block. This must fail if ì is not a Fixnum. You must not check if something is an Integer in your method, but in the block. This should work:
describe "Array" do
describe "new_map" do
it "should not call map" do
a = [1, 2, 3]
a.new_map { |i| (i.is_a?(Integer) ? i + 1 : i) }.should eq([2, 3, 4])
end
it "should map any object" do
a = [1, "two", :three]
a.new_map { |i| i.class }.should eq([Fixnum, String, Symbol])
end
end
end
class Array
def new_map
result = []
self.each do |item|
result << yield(item)
end
result
end
end
And both your tests pass, don't check object's class unnecessarily, trust duck typing.
I have a tree that I'm trying to traverse. As I traverse it, I keep a stack of enumerators in which each enumerator is used to enumerate over a tree's children.
I'd like to be able to duplicate this stack of enumerators and hand it to another object so it may traverse the tree starting in the place indicated by the state of the stack.
When I attempt to call #dup on Enumerator, I get an error. Is it possible to duplicate an Enumerator? If not, how could I accomplish the same thing? (I've considered a stack of integers as indices, but am worried about the efficiency.
Here's some code to show what I'm seeing...
Once the first enumerator has started, you cannot duplicate it. That is my situation.
a = [1,2,3].each
=> #<Enumerator: [1, 2, 3]:each>
a.next
=> 1
b = a.dup
TypeError: can't copy execution context
from (irb):3:in `initialize_copy'
from (irb):3:in `initialize_dup'
from (irb):3:in `dup'
from (irb):3
Implement your own enumerator class.
There’s not much magic to an enumerator beyond incrementing an internal counter.
class MyArrayEnumerator
def initialize(array)
#ary,#n=array,0
end
def next
raise StopIteration if #n == #ary.length
a=#ary[#n];#n+=1;a
end
end
class Array
def my_each
MyArrayEnumerator.new(self)
end
end
a = [1,2,3].my_each # => #<MyArrayEnumerator:0x101c96588 #n=0, #array=[1, 2, 3]>
a.next # => 1
b = a.dup # => #<MyArrayEnumerator:0x101c95ae8 #n=1, #array=[1, 2, 3]>
a.next # => 2
b.next # => 2
Use clone instead:
e1 = [1,2,3].each
e1.dup # TypeError: can't copy execution context
e2 = e1.clone
e1.next #=> 1
e2.next #=> 1
keep one "head" amongst Enumerator's instances, and store history for behind copies:
class Enum
def initialize()
#history = [] # history will be shared between instances
#history_cursor = -1
#head = Enumerator.new do |yielder|
#yielder = yielder
enumerate
end
end
def next
if #history_cursor < #history.count - 1
#history[#history_cursor += 1]
else
new_item #head.next
end
end
private
def new_item item
#history << item
#history_cursor = #history.count - 1
item
end
def enumerate
13.times do |i|
#yielder << i # yielder is shared between instances
end
end
end
Usage:
enum1 = Enum.new
p enum1.next # 0
enum2 = enum1.clone
p enum2.next # 1
p enum1.next # 1