Move to next iteration of a loop through a function inside the loop in ruby - ruby

I am trying to write a piece of code where i can move to the next iteration of a loop while inside a method called in the loop.
In sample code, this is what i am trying to do
def my a
if a > 3
next
end
end
x = [1,2,3,4,5,6,7]
for i in x
my i
print i
end
This gives a syntax error.
One way to achieve this is by raising an error and catching it.
def my a
if a > 3
raise "Test"
end
end
x = [1,2,3,4,5,6,7]
for i in x
begin
my i
print i
rescue Exception => e
#do nothing
end
end
But exceptions are expensive. I dont want to return anything from the function or set flag variables in the function because i want to keep the code clean of these flags.
Any ideas?

A Ruby way of having a function affect the caller's flow of control is for the caller to pass the function a block, which the function can then execute (or not):
def my a
yield unless a > 3
end
x = [1,2,3,4,5,6,7]
for i in x
my i do
print i
end
end
# => 123
See also: Blocks and yields in Ruby

Related

Ruby Pattern Program Error while printing output

I had a working logic to print pyramid and square dynamically by accepting the number of rows from terminal. I am facing error after including "module,classes and begin-end block".
module PatternPrinting
class Operation
def input
puts 'Enter the number of rows:'
rows = Integer(gets.chomp)
raise StandardError if rows <= 0 || rows > 10
pyramid(rows)
square(rows)
rescue StandardError
raise StandardError, 'Invalid Input, the entered value exceeds is not between 1-10 '
end
def pyramid(rows)
rows.times do |n|
print ' ' * (rows - n)
puts '*' * (2 * n + 1)
end
end
puts "Pyramid Rows: #{pyramid(rows)}"
def square(rows)
rows.times do |_n|
puts '*' * 10
end
end
puts "Sqaure Rows: #{square(rows)}"
end
end
begin
res = PatternPrinting::Operation.new
res.input
end
But I am facing error
pattern.rb:20:in `<class:Operation>': undefined local variable or method `rows' for PatternPrinting::Operation:Class (NameEr
ror)
from ./pattern.rb:3:in `<module:PatternPrinting>'
from ./pattern.rb:2:in `<main>'
rows is a local variable only available in the input method and nowhere else. Once that method completed, the local variables are lost.
If you want data to be available to all methods of a class object, you need to use instance variables.
Do
#rows = Integer.get_chomp
And then do
#rows.times do |n|
and
#rows.times do |_n|
You are missing a basic concept in ruby.
Read about ruby implicit and explicit receiver. https://www.reddit.com/r/ruby/comments/436d1m/what_is_the_difference_between_an_implicit_and/
Local variable has method/function scoping. So, at 20 rows is not visible.
Even though, let suppose your rows is accessible then also it should throw error coz, here implicit receiver self is your class PatternPrinting. PatternPrinting will try to invoke a method pyramid which is defined as class method https://github.com/rubocop-hq/ruby-style-guide#def-self-class-methods and your receiver PatternPrinting will not find the method and end up being calling method missing.
I highly recommend take a look https://rubymonk.com/.

callcc in ruby cause infinite loop?

I'm trying to review the slides of class. The code is supposed to print "early work" once then followed by "later work" twice(you can set the repeat number of the later work). But I wonder why this code doesn't work, and how can I modify the code? Since now the code will generate infinite loop of "later work" rather than 2(which is supposed to be)
require 'continuation'
def work
p "early work"
here = callcc {|here| here}
p "later work"
return here
end
def rework(k)
entry = work
k.times do |i|
entry.call(entry)
end
end
rework(2)
The code doesn't work because the loop counter in k.times is stuck. Each call to entry.call(entry) rewinds the program to when callcc returns. So callcc returns again, the later work happens again, work returns again, and k.times starts again. When k.times starts, it resets its loop counter to zero. The infinite loop is because the loop counter is always zero.
To fix the program, we must continue the loop, not restart it. The best fix is to use a fiber, but first, I try to use a continuation. Here's the version that works on my machine:
require 'continuation'
def work
p "early work"
here = callcc {|here| here}
p "later work"
return here
end
class Integer
def my_times
i = 0
while i < self
yield i
i += 1
end
end
end
def rework(k)
entry = nil
k.my_times do |i|
if i == 0
entry = work
else
entry.call(entry)
end
end
end
rework(2)
I fix the control flow by calling work inside the loop. When work returns again, I don't reset the loop counter.
I also define my own Integer#my_times and don't use Ruby's Integer#times. If I change the code from k.my_times back to k.times, the loop counter gets stuck again. This exposes a problem with continuation objects in Ruby.
When a continuation rewinds a program, it might rewind or preserve the values of local variables. My program assumes that entry.call preserves the loop counter. Matz's Ruby Implementation preserves the loop counter in Integer#my_times, but rewinds the loop counter in Integer#times. This is the only reason why my program can't use Integer#times.
MRI seems to rewind locals in C code (like Integer#times) but preserve locals in Ruby code (like Integer#my_times). This makes a mess of loop counters and other locals. Ruby does not fix this mess, but warns against callcc. Ruby says, warning: callcc is obsolete; use Fiber instead.
Here's the program using a fiber:
def work
p "early work"
here = Fiber.new do
while true
p "later work"
Fiber.yield
end
end
here.resume
return here
end
def rework(k)
entry = nil
k.times do |i|
if i == 0
entry = work
else
entry.resume
end
end
end
rework(2)

How to break from a nested loop to a parent loop that is more than one level above which requires a value provided by the nested loop

In the following situation:
xxx.delete_if do |x|
yyy.descend do |y| # This is a pathname.descend
zzz.each do |z|
if x + y == z
# Do something
# Break all nested loops returning to "xxx.delete_if do |x|" loop
# The "xxx.delete_if do |x|" must receive a "true" so that it
# can delete the array item
end
end
end
end
What is the best way to achieve this multiple nested break while making sure I can pass the true value so that the array item is deleted?
Maybe I should use multiple break statements that return true or use a throw/catch with a variable, but I don't know if those are the best answer.
This question is different from How to break from nested loops in Ruby? because it requires that the parent loop receives a value from the nested loop.
throw/catch (NOT raise/rescue) is the way I typically see this done.
xxx.delete_if do |x|
catch(:done) do
yyy.each do |y|
zzz.each do |z|
if x + y == z
# Do something
throw(:done, true)
end
end
end
false
end
end
In fact, the Pickaxe explicitly recommends it:
While the exception mechanism of raise and rescue is great for abandoning execution when things go wrong, it's sometimes nice to be able to jump out of some deeply nested construct during normal processing. This is where catch and throw come in handy. When Ruby encounters a throw, it zips back up the call stack looking for a catch block with a matching symbol. When it finds it, Ruby unwinds the stack to that point and terminates the block. If the throw is called with the optional second parameter, that value is returned as the value of the catch.
That said, max's #any? suggestion is a better fit for this problem.
You can use multiple break statements.
For example:
xxx.delete_if do |x|
result = yyy.each do |y|
result2 = zzz.each do |z|
if x + y == z
break true
end
end
break true if result2 == true
end
result == true
end
However I would definitely avoid this in your particular situation.
You shouldn't be assigning variables to the result of each. Use map, reduce, select, reject, any?, all?, etc. instead
It makes more sense to use any? to accomplish the same purpose:
xxx.delete_if do |x|
yyy.any? do |y|
zzz.any? do |z|
x + y == z
end
end
end

Rubocop rule: Never use 'do' with multi-line 'while

I have the following code
# colours a random cell with a correct colour
def colour_random!
while true do
col, row = rand(columns), rand(rows)
cell = self[row,col]
if cell.empty? then
cell.should_be_filled? ? cell.colour!(1) : cell.colour!(0)
break
end
end
end
it's not that important what's doing, although it should pretty obvious. The point is that Rubocop gives me a warning
Never use 'do' with multi-line 'while
Why should I not do that? How should I do it then?
while is a keyword,so you don't need to pass a block. Without do..end it will work fine. The below is fine
def colour_random!
while true
col, row = rand(columns), rand(rows)
cell = self[row,col]
if cell.empty? then
cell.should_be_filled? ? cell.colour!(1) : cell.colour!(0)
break
end
end
end
while is a keyword, and if you pass a block to it, like do..end, it still works as you asked it to do, by not throwing any error, rather just a warning. But it could be dangerous if you try to pass a Proc or Method object to it, and dynamically try to convert it to a block using & keyword, as we do generally. That means
# below code will work as expected just throwing an warning.
x = 2
while x < 2 do
#code
end
But if you try to do by mistake like below
while &block # booom!! error
The reason is while is a keyword, which don't support any to_proc method to satisfy your need. So it can be dangerous.
Ruby style guide also suggested that Never use while/until condition do for multi-line while/until
I think the reason is as Nobuyoshi Nakada said in the mailing list
loop is a kernel method which takes a block. A block introduces new local variable scope.
loop do
a = 1
break
end
p a #=> causes NameError
while doesn't.
while 1
a = 1
break
end
p a #=> 1
Ruby actually has a shortcut for while true: the loop statement.
def colour_random!
loop do
col, row = rand(columns), rand(rows)
cell = self[row,col]
if cell.empty? then
cell.should_be_filled? ? cell.colour!(1) : cell.colour!(0)
break
end
end
end

How could one block detect that its inside another block?

This is my code:
def block
puts "from block"
yield
end
block do
puts "from command line"
block do
end
end
Here is the output:
from block
from command line
from block
I wonder how the second block could detect that its inside another block (of the same method).
So that the output will be this instead:
from block 1
from command line
from block 2
Is this possible? Because I want the nested block to be aware of this and run some additional code.
Thanks!
You could keep track of the block level with an instance variable, increment it whenever you enter a block, and decrement it whenever you leave a block:
def block
#block_level ||= 0
#block_level += 1
puts "from block ##block_level"
yield
#block_level -= 1
end
This answer is mostly just for fun, I don't suggest you use it.
Ruby lets you inspect the call stack in the form of a backtrace, but only when an exception is raised. So let's raise an exception and then stick out our arm and catch it before it goes to anyone else, and then: the backtrace is all ours!!
Then all you need to do is search the backtrace (an array) for any method calls to our method named "block", and count them.
class InspectBacktrace < Exception
end
def block
raise InspectBacktrace
rescue InspectBacktrace => e
level = e.backtrace.count { |x| x =~ /in `block'/ }
puts "from block #{level}"
yield
end
block do
puts "from command line"
block do
puts "from command line"
block do
puts "from command line"
end
end
end
Output:
from block 1
from command line
from block 2
from command line
from block 3
from command line
Edit: I've since come across the Kernel#caller method which just gives you the current execution stack with no hassles. So the following code might be acceptable, as long as you don't have two methods named "block" in the same file that call each other:
def block
level = caller.count { |x| x =~ /^#{ Regexp.escape(__FILE__) }:\d+:in `block'$/ } + 1
puts "from block #{level}"
yield
end
What yjerem says, just use ensure to avoid troubles with exceptions, and it sounds like a global variable, not instance variable.
def block
begin
$block_level ||= 0
$block_level += 1
puts "from block #{$block_level}"
yield
ensure
$block_level -= 1
end
end

Resources