Can I have a block in begin/while statement in Ruby? - ruby

I'm trying out to have a block in a while and begin statements in Ruby, but I get a syntax error. Any other way to implement it?
Here's what I want to accomplish
(1..limit).each { |i|
while (true) do |n|
x = n * (i%n)
puts n if n%i != 0
break if x.even? && !x.zero?
n += 1
end
}

while is a keyword, so you do not need the block. Your code should be:
(1..limit).each { |i|
while (true)
x = n * (i%n)
puts n if n%i != 0
break if x.even? && !x.zero?
n += 1
end
}
But you are requesting a block variable form the while statement. Variable names inside the pipes are for variables passed to your block containing information from whatever calls your block. I will assume that n is supposed to increment. Here is a working version:
(1..limit).each { |i|
n = 0
while (true)
x = n * (i%n)
puts n if n%i != 0
break if x.even? && !x.zero?
n += 1
end
}
If you really need the code in a block, you could create one and then call it, like this (ruby 1.9 only):
(1..limit).each { |i|
n = 0
while (true)
-> do
x = n * (i%n)
puts n if n%i != 0
break if x.even? && !x.zero?
n += 1
end.()
end
}
By the way, here is a cleaner version:
(1..limit).each do |i|
n = 0
loop do
x = n * (i % n)
puts n if n % i != 0
break if x.even? and !x.zero?
n += 1
end
end

Related

Nuances of where to define a variable in ruby code

I've just started learning ruby, and the position of where variables are defined somewhat elude me. For example, why does this code work:
def two_sum(nums)
result = nil
i = 0
while i < nums.length
k = (nums.length - 1)
if nums[i] + nums[k] == 0
result = [i,k]
end
i += 1
k -= 1
end
return result
end
And why does this code not work:
def two_sum(nums)
result = nil
i = 0
k = (nums.length - 1)
while i < nums.length
if nums[i] + nums[k] == 0
result = [i,k]
end
i += 1
k -= 1
end
return result
end
Thank you in advance!
I think you code might just have a bug
while i < nums.length
k = (nums.length - 1)
...
k -= 1 # this statement has no effect!
end
Above, the value if k is always (nums.length - 1) because you reassign it at the begin of each iteration. The other statement has no effect.
k = (nums.length - 1)
while i < nums.length
...
k -= 1
end
Above, the value of k starts at (nums.length - 1) in the first iteration and is then reduced by 1 for each iteration.
Pro tipp —
It is very unusual in Ruby to use a for/while/until loop. If you want to loop over all elements use each or each_with_index instead
array.each { |each| ... }
array.each_with_index { |each, n| ... }

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"]

ruby code stopping at run time, seemingly an infinite loop

if i run the code, it will stop and not do anything and i am unable to type. seems to be an infinite loop.
the problem seems to be the end until loop, however if i take that out, my condition will not be met.
can anyone find a solution? i have tried all the loops that i can think of.
/. 2d array board ./
board = Array.new(10) { Array.new(10, 0) }
/. printing board ./
if board.count(5) != 5 && board.count(4) != 4 && board.count(3) != 3
for i in 0..9
for j in 0..9
board[i][j] = 0
end
end
aircraftcoord1 = (rand*10).floor
aircraftcoord2 = (rand 6).floor
aircraftalign = rand
if aircraftalign < 0.5
for i in 0..4
board[aircraftcoord2+i][aircraftcoord1] = 5
end
else
for i in 0..4
board[aircraftcoord1][aircraftcoord2+i] = 5
end
end
cruisercoord1 = (rand*10).floor
cruisercoord2 = (rand 7).floor
cruiseralign = rand
if cruiseralign < 0.5
for i in 0..3
board[cruisercoord2+i][cruisercoord1] = 4
end
else
for i in 0..3
board[cruisercoord1][cruisercoord2+i] = 4
end
end
destroyercoord1 = (rand*10).floor
destroyercoord2 = (rand 8).floor
destroyeralign = rand
if destroyeralign < 0.5
for i in 0..2
board[destroyercoord2+i][destroyercoord1] = 3
end
else
for i in 0..2
board[destroyercoord1][destroyercoord2+i] = 3
end
end
end until board.count(5) == 5 && board.count(4) == 4 && board.count(3) == 3
print " "
for i in 0..9
print i
end
puts
for i in 0..9
print i
for j in 0..9
print board[i][j]
end
puts
end
The line board.count(5) == 5 ... will never be true because board is a two-dimensional array. I can't tell what the condition should be, but it could look something like:
board[5].count(5) == 5

Finding the sum of the digits of a factorial

factorial_sum(5) should return 3. The error I'm getting is that "inject is an undefined method". I was also wondering if it's possible to combine the two functions. I wasn't sure as I am just starting out on recursion. Thanks!
def factorial_sum(x)
factorial = factorial(x)
factorial.to_s.split('').collect { |i| i.to_i }
sum = factorial.inject { |sum, n| sum + n }
end
def factorial(x)
if x < 0
return "Negative numbers don't have a factorial"
elsif x == 0
1
else
factorial = x * factorial(x - 1)
end
end
puts factorial_sum(5)
factorial.to_s.split('').collect { |i| i.to_i }
This line is a no-op. You build a list and then throw it away. You probably meant factorial = ...
I have to say though that this would be pretty easy to find with a little effort and some print statements...
By the way, here's a slightly more concise way:
(1..x).reduce(:*).to_s.chars.map(&:to_i).reduce(:+)
A direct way without temporarily converting it into strings, and without recursion.
s, q = 0, 120
while q > 0
q, r = q.divmod(10)
s += r
end
s # => 3

Evaluating exit condition at bottom of Ruby while loop

I have a very basic question about Ruby loops.
This program as written returns the ith prime number +1 (ie the example should return 17). I know I could simply return cand-1, but I was wondering what the "Ruby way" of checking if the answer has been found at the bottom of the while loop and only incrementing if it hasn't.
def ith_prime(i)
pI = 0 # primes index
divs = []
cand = 2
until pI == i do
if divs.find { |div| cand%div == 0 } == nil
divs << cand
pI += 1
end
cand += 1
end
cand
end
puts ith_prime(7)
> 18
I use loop instead of while or until most of the time. This way I can put the exit condition anywhere in the loop.
I would write it like that (if I understood the problem correctly):
def ith_prime(i)
pI = 0 # primes index
divs = []
cand = 2
loop do
unless divs.find { |div| cand%div == 0 }
divs << cand
pI += 1
end
break if pI == i
cand += 1
end
cand
end

Resources