Game has an array of ten Frame instances
class Frame
attr_accessor :rolls
def initialize
#rolls = ""
end
end
class Game
attr_accessor :frames
def initialize
#frames = Array.new(10, Frame.new)
end
def print_frames
#frames.each_with_index do |frame, idx|
p "Frame ##{idx+1}: #{frame.rolls}"
end
end
end
game = Game.new
rolls = [5, 5, 3, 7, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2]
curr = 0
rolls.each_with_index do |roll|
game.frames[curr].rolls << roll.to_s
if game.frames[curr].rolls.size == 2
curr += 1
end
end
p "Total rolls: #{rolls.size}"
p game.print_frames
I expect 10 lines to be printed, each line has a frame id with a string of 2 numbers
however, the following is returned
"Total rolls: 20"
"Frame #1: 55374000000000000022"
"Frame #2: 55374000000000000022"
"Frame #3: 55374000000000000022"
"Frame #4: 55374000000000000022"
"Frame #5: 55374000000000000022"
"Frame #6: 55374000000000000022"
"Frame #7: 55374000000000000022"
"Frame #8: 55374000000000000022"
"Frame #9: 55374000000000000022"
"Frame #10: 55374000000000000022"
[#<Frame:0x0000565402e40528 #rolls="55374000000000000022">, #<Frame:0x0000565402e40528 #rolls="55374000000000000022">, #<Frame:0x0000565402e40528 #rolls="55374000000000000022">, #<Frame:0x0000565402e40528 #rolls="55374000000000000022">, #<Frame:0x0000565402e40528 #rolls="55374000000000000022">, #<Frame:0x0000565402e40528 #rolls="55374000000000000022">, #<Frame:0x0000565402e40528 #rolls="55374000000000000022">, #<Frame:0x0000565402e40528 #rolls="55374000000000000022">, #<Frame:0x0000565402e40528 #rolls="55374000000000000022">, #<Frame:0x0000565402e40528 #rolls="55374000000000000022">]
instance variable is acting like class variables shared between frames. all 20 rolls numbers are concatenated instead of each frame owning a pair.
what is wrong with the code? is it because the Game or Frame object is not instantiated correctly?
Array.new(10, Frame.new)
Creates an array with 10 elements all pointing to a single Frame instance. To create an array with 10 separate Frame instances you should use the block form.
Array.new(10) { Frame.new }
This executes the block 10 times and assigns the result of each execution to the cosponsoring index.
See: Creating Arrays
An array can also be created by explicitly calling ::new with zero,
one (the initial size of the Array) or two arguments (the initial size
and a default object).
ary = Array.new #=> []
Array.new(3) #=> [nil, nil, nil]
Array.new(3, true) #=> [true, true, true]
Note that the second argument populates the array with references to
the same object. Therefore, it is only recommended in cases when you
need to instantiate arrays with natively immutable objects such as
Symbols, numbers, true or false.
To create an array with separate objects a block can be passed
instead. This method is safe to use with mutable objects such as
hashes, strings or other arrays:
Array.new(4) {Hash.new} #=> [{}, {}, {}, {}]
Array.new(4) {|i| i.to_s } #=> ["0", "1", "2", "3"]
The method Array.new it's defined as new(size=0, default=nil) that means the second argument would be the value for all the objects inside the array, default value.
that's why for your case all 10 objects are the same.
https://ruby-doc.org/core-2.7.0/Array.html#method-c-new
this snippet code could help you to resolve
#frames = []
10.times {|i| #frames.push(rand) }
=> [0.7053319996471655, 0.34131818323294594, 0.4084836724883256, 0.20452172335941388, 0.5124065818560665, 0.4203474973940552, 0.6719502264788891, 0.7453268015406016, 0.09500886225101768, 0.9053707563920769]
# try this for your case
# 10.times {|i| #frames.push(Frame.new) }
# using array creation with block
# ty tadman for the advice
# #frames = Array.new(10) { Frame.new }
Related
These were the instructions given on Codewars (https://www.codewars.com/kata/56b5afb4ed1f6d5fb0000991/train/ruby):
The input is a string str of digits. Cut the string into chunks (a chunk here is a substring of the initial string) of size sz (ignore the last chunk if its size is less than sz).
If a chunk represents an integer such as the sum of the cubes of its digits is divisible by 2, reverse that chunk; otherwise rotate it to the left by one position. Put together these modified chunks and return the result as a string.
If
sz is <= 0 or if str is empty return ""
sz is greater (>) than the length of str it is impossible to take a chunk of size sz hence return "".
Examples:
revrot("123456987654", 6) --> "234561876549"
revrot("123456987653", 6) --> "234561356789"
revrot("66443875", 4) --> "44668753"
revrot("66443875", 8) --> "64438756"
revrot("664438769", 8) --> "67834466"
revrot("123456779", 8) --> "23456771"
revrot("", 8) --> ""
revrot("123456779", 0) --> ""
revrot("563000655734469485", 4) --> "0365065073456944"
This was my code (in Ruby):
def revrot(str, sz)
# your code
if sz > str.length || str.empty? || sz <= 0
""
else
arr = []
while str.length >= sz
arr << str.slice!(0,sz)
end
arr.map! do |chunk|
if chunk.to_i.digits.reduce(0) {|s, n| s + n**3} % 2 == 0
chunk.reverse
else
chunk.chars.rotate.join
end
end
arr.join
end
end
It passed 13/14 test and the error I got back was as follows:
STDERR/runner/frameworks/ruby/cw-2.rb:38:in `expect': Expected: "", instead got: "095131824330999134303813797692546166281332005837243199648332767146500044" (Test::Error)
from /runner/frameworks/ruby/cw-2.rb:115:in `assert_equals'
from main.rb:26:in `testing'
from main.rb:84:in `random_tests'
from main.rb:89:in `<main>'
I'm not sure what I did wrong, I have been trying to find what it could be for over an hour. Could you help me?
I will let someone else identify the problem with you code. I merely wish to show how a solution can be speeded up. (I will not include code to deal with edge cases, such as the string being empty.)
You can make use of two observations:
the cube of an integer is odd if and only if the integer is odd; and
the sum of collection of integers is odd if and only if the number of odd integers is odd.
We therefore can write
def sum_of_cube_odd?(str)
str.each_char.count { |c| c.to_i.odd? }.odd?
end
Consider groups of 4 digits in the last example, "563000655734469485".
sum_of_cube_odd? "5630" #=> false (so reverse -> "0365")
sum_of_cube_odd? "0065" #=> true (so rotate -> "0650")
sum_of_cube_odd? "5734" #=> true (so rotate -> "7345")
sum_of_cube_odd? "4694" #=> true (so rotate -> "6944")
so we are to return "0365065073456944".
Let's create another helper.
def rotate_chars_left(str)
str[1..-1] << s[0]
end
rotate_chars_left "0065" #=> "0650"
rotate_chars_left "5734" #=> "7345"
rotate_chars_left "4694" #=> "6944"
We can now write the main method.
def revrot(str, sz)
str.gsub(/.{,#{sz}}/) do |s|
if s.size < sz
''
elsif sum_of_cube_odd?(s)
rotate_chars_left(s)
else
s.reverse
end
end
end
revrot("123456987654", 6) #=> "234561876549"
revrot("123456987653", 6) #=> "234561356789"
revrot("66443875", 4) #=> "44668753"
revrot("66443875", 8) #=> "64438756"
revrot("664438769", 8) #=> "67834466"
revrot("123456779", 8) #=> "23456771"
revrot("563000655734469485", 4) #=> "0365065073456944"
It might be slightly faster to write
require 'set'
ODD_DIGITS = ['1', '3', '5', '7', '9'].to_set
#=> #<Set: {"1", "3", "5", "7", "9"}>
def sum_of_cube_odd?(str)
str.each_char.count { |c| ODD_DIGITS.include?(c) }.odd?
end
Consider the following irb loop:
irb(main):015:0> [nil, nil].reduce(0) { |accum, x| accum + 1 unless x.nil? }
=> nil
Why does this return nil instead of 0?
According to the Ruby Enumerable documentation:
If you specify a block, then for each element in enum the block is passed an accumulator value (memo) and the element. If you specify a symbol instead, then each element in the collection will be passed to the named method of memo. In either case, the result becomes the new value for memo. At the end of the iteration, the final value of memo is the return value for the method.
My expectation would be that the accumulator should get set to 0 before the array starts to get folded, since that is given as the initial value. Then, the block's escape clause will trigger for all elements in this array, so the accumulator will never change. Finally, since 0 is the last value stored for the accumulator, it should be returned.
Whatever the block returns is going to be the next accumulator value.
And you return nil:
'whatever' unless true #=> nil
You could do this:
arr.reduce(0) { |a, e| e.nil? ? a : a + 1 }
Or this:
arr.compact.reduce(0) { |a, e| a + 1 }
Or this:
arr.compact.size
Reduce starts with the accumulator you pass but then sets it to whatever the block returns.
It might be helpful to see what it does internally (this is not the actual source code but a simple reproduction):
class Array
def my_reduce(memo, &blk)
each { |i| memo = blk.call(memo, i) }
memo
end
end
Here's some examples to show its usage:
# with 0 as starting memo
[nil, nil].reduce(0) { |memo, i| i ? memo + 1 : memo } # => 0
[nil, nil].reduce(0) { |memo, i | memo += 1 if i; memo; } # => 0
[nil, nil].reduce(0) { |memo, i| memo + (i ? 1 : 0) } # => 0
# with [] as starting memo
[1,2].reduce([]) { |memo, i| memo.push(i + 1); memo } # => [2,3]
[1,2].reduce([]) { |memo, i| memo.concat([i + 1]) } # => [2,3]
[1,2].reduce([]) { |memo, i| memo + [i + 1] } # => [2,3]
[1,2].reduce([]) { |memo, i| [*memo, i + 1] } # => [2,3]
You can see how only some of these require memo to be returned as the last line. The ones that don't are taking advantage of methods which return their modified objects, instead of relying on mutability (memo.push) or local variable assignment (memo += 1)
each_with_object is basically the same thing as reduce except that it automatically returns the accumulator from each block, and reverses the order of the block args (|i, memo| instead of |memo, i). It can be nice syntactic sugar for reduce when the memo is a mutable object. Returning the memo from the block is no longer necessary in the following example:
[1,2].each_with_object([]) { |i, memo| memo.push(i + 1) } # => [2,3]
However it won't work with your original example, because the memo (a number) is immutable:
# returns 0 but should return 1
[true, nil].each_with_object(0) { |i, memo| memo += 1 if i }
to say memo += 1 here is nothing but local variable assignment. Remember, you can never change the value of self for an object in ruby, not even a mutable one. If Ruby had increment operators (i++) then it might be a different story (see no increment operator in ruby)
Your expectations are correct but the missing piece is considering what the block returns after execution.
In Ruby the last thing to execute is returned and this code: accum + 1 unless x.nil? returns nil.
Just for science here's an example:
irb(main):051:0> puts 'ZOMG' unless nil.nil?
=> nil
Because nil is returned by the block your accumulator's initial 0 is overwritten with nil.
If you modify the code to return the accumulator you will get 0 as expected:
irb(main):052:0> [nil, nil].reduce(0) do |accum, x|
irb(main):053:1* accum + 1 unless x.nil?
irb(main):054:1> accum
irb(main):055:1> end
=> 0
I'm trying to use a while loop inside #inject. However, the last memo becomes nil at some point and I don't understand why. Here is my example (I use #each on the example just to show the expected result):
class TestClass
BASE_ARRAY = [5, 1]
def test_method(value)
result = []
BASE_ARRAY.each do |item|
while item <= value
result << item
value -= item
end
end
result
end
def test_method_alternate(value)
BASE_ARRAY.inject do |memo, item|
while item <= value
p memo
# memo << item (if left, returns NoMethodError for nil Class)
value -= item
end
end
end
end
solution_one = TestClass.new.test_method(11)
p solution_one # => [5, 5, 1]
solution_two = TestClass.new.test_method_alternate(11)
p solution_two
# => []
[]
nil
How does the accumulator become nil?
You're getting nil initially from the while loop:
The result of a while loop is nil unless break is used to supply a value.
That result becomes the result of other statements in a chain:
while
-> do |memo, item|
-> BASE_ARRAY.inject
-> test_method_alternate(11)
-> solution_two
To have .inject fill up an array, you'll want to provide an empty array to use as the first memo:
BASE_ARRAY.inject([]) do |memo, item|
# ... ^^^^
Then, be sure the array is the result of the block:
... do |memo, item|
while item <= value
memo << item
value -= item
end
memo # <---
end
Two things:
You need to initialize the memo with a value, in this case, you'll want a [].
You need to return the memo on each iteration of inject.
So, you should get your desired result of [5, 5, 1] by changing your method to be like this:
def test_method_alternate(value)
BASE_ARRAY.inject([]) do |memo, item|
while item <= value
memo << item
value -= item
end
memo
end
end
I'd like to create a subclass of Range in order to specify a step size other than 1 so I can do things like:
>> a = RangeWithStepSize.new(-1, 2, 0.5).each {|x| puts(x)}
-1.0
-0.5
0.0
0.5
1.0
1.5
2.0
=> -1..2
My first attempt at an implementation doesn't work:
class RangeWithStepSize < Range
attr_reader :step_size
def initialize(start_v, end_v, step_size, exclusive = false)
super(start_v, end_v, exclusive)
#step_size = step_size
end
def each
self.step(step_size).each
end
end
>> a = RangeWithStepSize.new(-1, 2, 0.5).each {|x| puts(x)}
=> #<Enumerator: [-1.0, -0.5, 0.0, 0.5, 1.0, 1.5, 2.0]:each>
It appears that RangeWithStepSize#each is returning a valid enumerator, but it doesn't enumerate. Any idea why?
<aside>This may be related, but I notice is that Range#step without a block does NOT return an enumerator as specified in the documentation; it returns an array instead:
>> Range.new(-1, 2).step(0.5).class
=> Array
An Array is enumerable, but it is not an Enumerator. Is this a documentation bug?</aside>
clarification
I'd like to make a version of Range that encapsulates a step size, so I can do:
a = RangeWithStepSize(-1, 2, 0.5)
b = RangeWithStepSize(-1, 2, 0.25)
... so enumerating on a produces a step size of 0.5 and b produces 0.25.
You know you can do this, right? Inheritance isn't necessary here.
(-1..2).step(0.5) do |x|
puts x
end
Your code will work with a few small adjustments:
class RangeWithStepSize < Range
attr_reader :step_size
def initialize(start_v, end_v, step_size, exclusive = false)
super(start_v, end_v, exclusive)
#step_size = step_size
end
def each (&block)
self.step(step_size).each(&block)
end
end
Let's say, I want to separate certain combinations of elements from an array. For example
data = %w{ start before rgb 255 255 255 between hex FFFFFF after end }
rgb, hex = [], []
data.each_with_index do |v,i|
p [i,v]
case v.downcase
when 'rgb' then rgb = data.slice! i,4
when 'hex' then hex = data.slice! i,2
end
end
pp [rgb, hex, data]
# >> [0, "start"]
# >> [1, "before"]
# >> [2, "rgb"]
# >> [3, "hex"]
# >> [4, "end"]
# >> [["rgb", "255", "255", "255"],
# >> ["hex", "FFFFFF"],
# >> ["start", "before", "between", "after", "end"]]
The code have done the correct extraction, but it missed the elements just after the extracted sets. So if my data array is
data = %w{ start before rgb 255 255 255 hex FFFFFF after end }
then
pp [rgb, hex, data]
# >> [["rgb", "255", "255", "255"],
# >> [],
# >> ["start", "before", "hex", "FFFFFF", "after", "end"]]
Why does it happen? How to get those missed elements inside #each_with_index? Or may be there is a better solution for this problem assuming that there are much more sets to extract?
The problem is that you are mutating the collection while you are iterating over it. This cannot possibly work. (And in my opinion, it shouldn't. Ruby should raise an exception in this case, instead of silently allowing incorrect behavior. That's what pretty much all other imperative languages do.)
This here is the best I could come up with while still keeping your original style:
require 'pp'
data = %w[start before rgb 255 255 255 hex FFFFFF after end]
rgb_count = hex_count = 0
rgb, hex, rest = data.reduce([[], [], []]) do |acc, el|
acc.tap do |rgb, hex, rest|
next (rgb_count = 3 ; rgb << el) if /rgb/i =~ el
next (rgb_count -= 1 ; rgb << el) if rgb_count > 0
next (hex_count = 1 ; hex << el) if /hex/i =~ el
next (hex_count -= 1 ; hex << el) if hex_count > 0
rest << el
end
end
data.replace(rest)
pp rgb, hex, data
# ["rgb", "255", "255", "255"]
# ["hex", "FFFFFF"]
# ["start", "before", "after", "end"]
However, what you have is a parsing problem and that should really be solved by a parser. A simple hand-rolled parser/state machine will probably be a little bit more code than the above, but it will be so much more readable.
Here's a simple recursive-descent parser that solves your problem:
class ColorParser
def initialize(input)
#input = input.dup
#rgb, #hex, #data = [], [], []
end
def parse
parse_element until #input.empty?
return #rgb, #hex, #data
end
private
def parse_element
parse_color or parse_stop_word
end
def parse_color
parse_rgb or parse_hex
end
def parse_rgb
return unless /rgb/i =~ peek
#rgb << consume
parse_rgb_values
end
I really like recursive-descent parsers because their structure almost perfectly matches the grammar: just keep parsing elements until the input is empty. What is an element? Well, it's a color specification or a stop word. What is a color specification? Well, it's either an RGB color specification or a hex color specification. What is an RGB color specification? Well, it's something that matches the Regexp /rgb/i followed by RGB values. What are RGB values? Well, it's just three numbers …
def parse_rgb_values
3.times do #rgb << consume.to_i end
end
def parse_hex
return unless /hex/i =~ peek
#hex << consume
parse_hex_value
end
def parse_hex_value
#hex << consume.to_i(16)
end
def parse_stop_word
#data << consume unless /rgb|hex/i =~ peek
end
def consume
#input.slice!(0)
end
def peek
#input.first
end
end
Use it like so:
data = %w[start before rgb 255 255 255 hex FFFFFF after end]
rgb, hex, rest = ColorParser.new(data).parse
require 'pp'
pp rgb, hex, rest
# ["rgb", 255, 255, 255]
# ["hex", 16777215]
# ["start", "before", "after", "end"]
For comparison, here's the grammar:
S → element*
element → color | word
color → rgb | hex
rgb → rgb rgbvalues
rgbvalues → token token token
hex → hex hexvalue
hexvalue → token
word → token
Because you are manipulating data in place.
When you hit rgb the next element in the loop would be 255, but you are deleting those elements so now between is in the place that rgb was, so the next element is hex
Something like this may work better for you:
when 'rgb' then rgb = data.slice! i+1,3
when 'hex' then hex = data.slice! i+1,1
Here is a bit nicer solution
data = %w{ start before rgb 255 255 255 hex FFFFFF hex EEEEEE after end }
rest, rgb, hex = [], [], []
until data.empty?
case (key = data.shift).downcase
when 'rgb' then rgb += [key] + data.shift(3)
when 'hex' then hex += [key] + data.shift(1)
else rest << key
end
end
p rgb, hex, rest
# >> ["rgb", "255", "255", "255"]
# >> ["hex", "FFFFFF", "hex", "EEEEEE"]
# >> ["start", "before", "after", "end"]