How to refactor consecutive, independent, if statements in this algorithm? - ruby

The following method generates 81 Cell objects that each have their distinct row, column, and blocks. It uses an algorithm that changes those attributes based on the Cell that's currently be generated.
The 1.times do portion is implemented to skip that block of if statements on the first loop inside the until loop, when the cell_counter is 0. How do I make this more elegant?
def initialize_default_cells
cell_counter, row, column = 0,0,0
block = 1
until cell_counter == 81
1.times do
break if cell_counter == 0
if cell_counter % 1 == 0
column += 1
end
if cell_counter % 3 == 0
block += 1
end
if cell_counter % 9 == 0
column -= 9
row += 1
block -= 3
end
if cell_counter % 27 == 0
block += 3
end
end
#cells << Cell.new(ROW_ID[row], COLUMN_ID[column], block)
cell_counter += 1
end
end

I concluded that it was easiest to calculate row, column and block from scratch for each i = 0,..,80.
def initialize_default_cells
(0..80).each do |i|
#cells << Cell.new(ROW_ID[i/9], COLUMN_ID[i%9], 1 + (i%9)/3 + 3*(i/27))
end
end
The key for COLUMN_ID (i%9) is reduced from i-9*(i/9) and the last argument (1 + (i%9)/3 + 3*(i/27)) is reduced from 1 + i/3 - 3*(i/9) + 3*(i/27)
Consider three examples.
i=0
#cells << Cell.new(ROW_ID[0/9], COLUMN_ID[0%9], 1 + (0%9)/3 + 3*(0/27))
#=> << Cell.new(ROW_ID[0], COLUMN_ID[0], 1)
i=6
#cells << Cell.new(ROW_ID[6/9], COLUMN_ID[6%9], 1 + (6%9)/3 + 3*(6/27))
#=> << Cell.new(ROW_ID[0], COLUMN_ID[6], 3)
i=29
#cells << Cell.new(ROW_ID[29/9], COLUMN_ID[29%9], 1 + (29%9)/3 + 3*(29/27))
# << Cell.new(ROW_ID[3], COLUMN_ID[2], 4)
When i=6, 6/3 #=> 2 is the number of positive numbers that are divisible by 3, 6/9 #=> 0 is the number of positive numbers that are divisible by 9 and 6/27 #=> 0 is the number of positive numbers that are divisible by 27. The arguments of Cell::new are then computed with these values.

Related

How does recursion work in ruby?

I have this method which switches the number digit 5 with 7.
def switch_digit(num)
if num <= 0
return 0
end
digit = num % 10
if (digit == 5)
digit = 7
end
return switch_digit(num/10) * 10 + digit
end
switch_digit(5952)
Can someone explain why once the method hits the base case it doesn't return 0?
How does this recursive method actually work? Does it append the returned digit with the next digit?
I added a little change to your code, to be aware it's working.
Finally I also expected the value of the method was 0, but it is not.
The end is reached, but the returned value is not 0. Why?
def switch_digit(num, array)
if num <= 0
array << num
p array
puts "The end"
return 0
end
digit = num % 10
array << [digit, num]
if (digit == 5)
digit = 7
end
return p switch_digit(num/10, array) * 10 + digit
end
p "returned value = " + switch_digit(123456789, Array.new).to_s
Which outputs:
#=> [[9, 123456789], [8, 12345678], [7, 1234567], [6, 123456], [5, 12345], [4, 1234], [3, 123], [2, 12], [1, 1], 0]
#=> The end
#=> 1
#=> 12
#=> 123
#=> 1234
#=> 12347
#=> 123476
#=> 1234767
#=> 12347678
#=> 123476789
#=> "returned value = 123476789"
The base case returns 0, but the overall result is determined by the return switch_digit(num/10) * 10 + digit
Follow the code through with a smaller example e.g. switch_digit(15):
num <= 0 # no
digit = num % 10 # 5
digit == 5 # yep, so swap it for a 7
return switch_digit(num/10) * 10 + 7
num/10 is 1 so what does the recursive switch_digit(1) evaluate to?
num <= 0 # no
digit = num % 10 # 1
digit == 1 # so leave it unchanged
return switch_digit(num/10) * 10 + 1
num/10 is 0 so now we hit the base case
switch_digit(15) == switch_digit(1) * 10 + 7
switch_digit(1) == switch_digit(0) * 10 + 1
switch_digit(0) == 0 # the base case
working back up, plugging in values from lower down results:
switch_digit(1) == 0 * 10 + 1 == 1
switch_digit(15) == 1 * 10 + 7 == 17
I'd also add that there's nothing specific to Ruby about how recursion is handled here. Any other descriptions of recursion, or classic example such as a recursive factorial function should help you get a better understanding.

Ruby FizzBuzz using arrays, my logic seems right but it is getting an error

FizzBuzz, a classic problem, returns all the numbers up to N with a slight twist. If a number is divisible by 3, it is replaced with "fizz". If it's divisible by 5, it's replaced with "buzz". If it's divisible by both, it's replaced with "fizzbuzz"
I keep getting this Error message:
comparison of Fixnum with nil failed
Can someone explain this error message to me please? Also why is the code not working?
def fizz_buzz(n)
arr = (1..n).to_a
i = 0
while arr[i] < arr[n]
if i % 3 == 0 && i % 5 == 0
arr[i] = 'fizzbuzz'
elsif i % 3 == 0
arr[i] = 'fizz'
elsif i % 5 == 0
arr[i] = 'buzz'
else
arr[i] = i
i += 1
end
end
return arr
end
fizz_buzz(12)
Your conditions are just a bit off, give this a try:
def fizz_buzz(n)
arr = (1..n).to_a
i = 0
while i < n
if arr[i] % 3 == 0 && arr[i] % 5 == 0
arr[i] = 'fizzbuzz'
elsif arr[i] % 3 == 0
arr[i] = 'fizz'
elsif arr[i] % 5 == 0
arr[i] = 'buzz'
end
i+=1
end
return arr
end
Trying to access arr[n] puts you outside the bounds of the array which returns nil in Ruby.
You can update the code the ruby way, by using blocks and guards, I don't remember last time I used a while loop in ruby :)
Also, Array.new accepts a block as an argument which you can exploit to build your Array in a single step:
def fizz_buzz(n)
Array.new(n) do |index|
x = index + 1
case
when x % 3 == 0 && x % 5 == 0 then "fizzbuzz"
when x % 3 == 0 then "fizz"
when x % 5 == 0 then "buzz"
else x
end
end
end
Notice I used 1 as a base index and not 0, you can just remove x = index + 1 and replace x with index to have it working in a zero index base
A solution with a block instead of the while loop, and guards
def fizz_buzz(n)
arr = (1..n).to_a
0.upto(n - 1) do |i|
arr[i] = "fizzbuzz" and next if i % 3 == 0 && i % 5 == 0
arr[i] = "fizz" and next if i % 3 == 0
arr[i] = "buzz" if i % 5 == 0
end
arr
end
#brad-melanson beat me to the straight-forward answer to your question, so I'll share an answer which uses some common Ruby idioms (passing a range to the Array constructor and map), which simplify things, prevent you from having to do any iteration bookkeeping and prevent the possibility of off-by-one errors, out-of-bounds errors, etc.
def fizz_buzz(n)
Array(1..12).map do |n|
if n % 3 == 0 && n % 5 == 0
'fizzbuzz'
elsif n % 3 == 0
'fizz'
elsif n % 5 == 0
'buzz'
else
n
end
end
end
result = fizz_buzz 12
# result => [1, 2, "fizz", 4, "buzz", "fizz", 7, 8, "fizz", "buzz", 11, "fizz"]

I have a issue.. Appending

I have this code:
1 #!/local/usr/bin/ruby
2
3 users = (1..255).to_a
4
5 x = " "
6 y = " "
7 z = " "
8 #a = " "
9
10 count = 1
11 users.each do |i|
12 x << i if count == 1
13 y << i if count == 2
14 z << i if count == 3
15 # if x.length == 60
16 # a << i if count == 1
17 # a << i if count == 2
18 # a << i if count == 3
19 # else
20 # end
21 if count == 3
22 count = 1
23 else
24 count += 1
25 end
26 end
27
28 puts x.length
29 puts y.length
30 puts z.length
31 #puts a.length
32
What this code does is append The numbers 1-255 into three different strings and outputs how many numbers are in each string.
IT WORKS
Example of working code:
[user#server ruby]$ ruby loadtest.rb
86
86
86
[user#server ruby]$
Now what I want it to do is have a failsafe called a as seen above, commented out, What I want is this, if each string contains 60 numbers I want it to append into the a string until there are no more numbers.
When I try to do it with the commented out section it outputs this:
[user#server ruby]$ ruby loadtest.rb
86
86
86
4
[user#server ruby]$ ruby loadtest.rb
WHY?! What am I doing wrong?
What this code does is append The numbers 1-255 into three different strings and outputs how many numbers are in each string.
After reducing the number of values being iterated for readability, here's what it's doing:
users = (1..5).to_a
x = " "
y = " "
z = " "
count = 1
users.each do |i|
x << i if count == 1 # => " \u0001", nil, nil, " \u0001\u0004", nil
y << i if count == 2 # => nil, " \u0002", nil, nil, " \u0002\u0005"
z << i if count == 3 # => nil, nil, " \u0003", nil, nil
if count == 3
count = 1
else
count += 1
end
end
x # => " \u0001\u0004"
y # => " \u0002\u0005"
z # => " \u0003"
puts x.length
puts y.length
puts z.length
# >> 3
# >> 3
# >> 2
Your code is creating binary inside the strings, not "numbers" as we normally think of them, as digits.
Moving on, you can clean up your logic using each_with_index and case/when. To make the results more readable I switched from accumulating into strings into arrays:
users = (1..5).to_a
x = []
y = []
z = []
users.each_with_index do |i, count|
case count % 3
when 0
x << i
when 1
y << i
when 2
z << i
end
end
x # => [1, 4]
y # => [2, 5]
z # => [3]
puts x.length
puts y.length
puts z.length
# >> 2
# >> 2
# >> 1
The real trick in this is the use of %, which does a modulo on the value.
... if each string contains 60 numbers I want it to append into the a string until there are no more numbers
As written, you are unconditionally appending to x,y,z even after they hit your limit.
You need to add a conditional around this code:
x << i if count == 1
y << i if count == 2
z << i if count == 3
so that it stops appending once it hits your limit.
By the looks of the else block that does nothing, I think you were headed in that direction:
if x.length == 60
a << i if count == 1
a << i if count == 2
a << i if count == 3
else
x << i if count == 1
y << i if count == 2
z << i if count == 3
end
Even that, though, won't do exactly what you want.
You'll want to check the string you are appending to to see if it has hit your limit yet.
I'd suggest refactoring to make it cleaner:
users.each do |i|
target_string = case count
when 1 then x
when 2 then y
when 3 then z
end
target_string = a if target_string.length == 60
target_string << i
if count == 3
count = 1
else
count += 1
end
end
It may be better to use an array instead of string as you are pushing numbers into those variables.
Let me propose a solution which achieves more or less what you are trying to do, but uses few Ruby tricks that may be useful in future.
x, y, z = r = Array.new(3) {[]}
a = []
iter = [0,1,2].cycle
(1..255).each do |i|
r.all? {|i| i.size == 60} ? a << i : r[iter.next] << i
end
p x.size, y.size, z.size
p a.size
Let's define our arrays. Even though I have arrays x, y, and z, they are there only because they were present in your code - I think we just need three arrays, each of which would collect numbers as they are picked from a range of numbers - between 1 to 255 - one by one. x,y,z = r uses parallel assignment technique and is equivalent to x,y,z = r[0],r[1],r[2]. Also, use of Array.new(3) {[]} helps in creating the Array of Array such that when we access r[1] it is initialized with empty array([]) by default.
x, y, z = r = Array.new(3) {[]}
a = []
In order to determine which array the next number picked from range has to be placed in, we will use an Enumerator generated from Enumerable#cycle. This enumerator is special - because it is soft of infinite in nature - and we can keep asking it to give an element by calling next, and it will cycle through the array elements of [0,1,2] - returning us 0,1,2,0,1,2,0,1,2... infinitely.
iter = [0,1,2].cycle
Next, we will iterate through the range of numbers 1..255. During each iteration, we will check whether all the 3 arrays in which we are collecting number have desired size of 60 with the help of Enumerable#all? - if so, we will append the number to array a - else we will assign it to one of the sub arrays of r based on the array index returned by iter enumerator.
(1..255).each do |i|
r.all? {|i| i.size == 60} ? a << i : r[iter.next] << i
end
Finally, we print the size of each of the array.
p x.size, y.size, z.size
#=> 60, 60, 60
p a.size
#=> 75

How to I make floyd triangle shape using for loop?

I need output of Floyd triangle like:
1
0 1
1 0 1
0 1 0 1
I tried. I didn't get it exactly. Can anyone explain the logic?
This is the code I tried:
k = 0
for i in 1..5
for j in 1..5
if (i%2)==0;
k = (j%2==0) ? 1:0;
else;
k = (j%2==0) ? 0:1;
puts k,'';
end
end
puts
end
The main issue here is that in order to get the "triangle" shape of your output, you need your inner loop to increment from 1 to i instead of 1 to 5.
k = 0
for i in 1..5
for j in 1..i
if (i%2)==0
k = j + 1
else
k = j
end
print "#{k%2} "
end
puts
end
Here's a one line approach:
5.times {|line| puts (line + 1).times.with_object(""){|num, str| (num + line).even? ? (str << " 1 ") : (str << " 0 ") } }
to make it more clear:
lines = 5
lines.times do |line|
str = ""
line = line + 1 # 5.times runs from 0 to 4 and we need 1 to 5
line.times do |num|
# the condition is a bit different because I changes the code a bit
if (line + num).even?
str << " 0 "
else
str << " 1 "
end
end
puts str
end
Alright the following should work, but i hope it's readable. If you need more explanation or have specific questions let me know
i = 1
while i <= 4 do
if i%2 > 0
output = 1
else
output = 0
end
j = 1
while j <= i do
print( "#{output} " )
if output == 1
output = 0
else
output = 1
end
j+=1
end
print( "\n" )
i+=1
end
You can try following code for output you are expecting:
k = 0
for i in 1..4
for j in 1..i // inner loop code runs i times for each outer loop iteration
if (i%2)==0;
k = (j%2==0) ? 1:0;
else;
k = (j%2==0) ? 0:1;
end
print k,' ';
end
puts
end
Click Here to see output.
You can also get idea about for loops through this link.
The prefered ruby way:
layers = 4 # Change to as many layers as you want
layers.times do |i| # i starts from 0
(i + 1).times do |j| # j also starts from 0
print (i + j + 1) & 1, ' '
end
puts
end
The for way:
layers = 4
for i in 0...layers
for j in 0...(i + 1)
print (i + j + 1) & 1, ' '
end
puts
end

adding 1 to each element of an array

i tred creating a method called "Sum_plus_one" that accepts an array argument containing integers. Themethod should return the sum of the integers in the array after adding one to each of them.
example:
sum_plus_one([1,2,3])
result should be: 9
my code looks like this
def sum_plus_one(*nums)
for num in nums
num + 1
end
total = 0
for num in nums
total += num
end
return total
end
Why not do a little bit of math beforehand and see that summing the array-elements-plus-one is the same as summing the elements and then adding the array length? For example:
(5+1) + (6+1) + (11+1) = 5 + 6 + 11 + (1 + 1 + 1)
= 5 + 6 + 11 + 3
That gives you something nice and simple:
array.inject(:+) + array.length
map/reduce is handy here:
def sum_plus_one(nums)
nums.map(&:succ).reduce(:+)
end
Edit:
here is one way to make your code work:
def sum_plus_one(nums)
nums.map! do |num|
num + 1
end
total = 0
for num in nums
total += num
end
return total
end
Functional Style Version
[1, 2, 3].reduce(0) {|acc, n| acc + n + 1}
Use Enumerable#inject
[105] pry(main)> arr
=> [1, 2, 3]
[106] pry(main)> arr.inject(0) { |var, i| var + i + 1 }
=> 9
So the method would look like
def sum_plus_one(*nums)
nums.inject(0) { |var, num| var + num + 1 }
end
your problem is that you need to assign your num + 1 value to the corresponding element of the array that you are enumerating in the first loop.
maybe something like this:
for i in (0...nums.count)
nums[i] += 1
end
after that your program should work
(yes, you can use fancy libraries or constructs instead of the above loop)
please note that you can eliminate the top loop and just add num + 1 to your total in the second loop.

Resources