Execute next from method - ruby

This code:
def skip_if_three(count)
puts 'three detected, let\'s skip this loop!' if count == 3
end
5.times do |count|
skip_if_three(count)
puts count
end
returns:
0
1
2
three detected, let's skip this loop!
3 # don't want this to appear!
4
However, if utilize the next keyword and do this:
def skip_if_three(count)
next if count == 3
end
5.times do |count|
skip_if_three(count)
puts count
end
I get this SyntaxError:
Invalid next
which was sort of expected. But how do I use next from a helper?
Update
I'm using nested loops and need to execute my check in each loop, so I want to keep it DRY, hence the external method.
5.times do |i|
skip_if_three(i)
puts count
5.times do |j|
skip_if_three(j)
puts count
end
end

def skip_if_three(count)
return unless count == 3
puts "three detected, let's skip this loop!"
throw(:three)
end
5.times do |count|
catch(:three) do
skip_if_three(count)
puts count
end
end
result:
0
1
2
three detected, let's skip this loop!
4
def three?(count)
return unless count == 3
puts "three detected, let's skip this loop!"
true
end
5.times do |count|
puts count unless three?(count)
end
result:
0
1
2
three detected, let's skip this loop!
4
def three?(count)
return unless count == 3
puts "three detected, let's skip this loop!"
true
end
5.times do |count|
next if three?(count)
puts count
end
result:
0
1
2
three detected, let's skip this loop!
4

A better solution would be to redesign the code blocks so that you don't have this issue. Hiding functionality like next isn't ideal, so something like this would retain the concision of your mockup code while making it clear what's actually going on:
def is_three? count
count == 3
end
5.times do |count|
next if is_three? count
puts count
end

Related

How do print odd numbers using block { }

I am trying to print my array with just odd numbers using the block method but i am not too sure how to.
I can print odd numbers using no block but do but do not know how to implement it into the block method { }
#non block method
array = [1,2,3,4,5,6,7,8]
array.each do |i|
if i % 2 == 0
puts "#{i}"
end
end
#output of 2 4 6 8
#block method not sure how
array = [1,2,3,4,5,6,7,8]
array.each {|i| put i if i % 2 == 0 end }
#expected output should be 2 4 6 8
Thank you in advanced !
your block is almost correct you just need to remove the end as it's an inline (or trailing) if method, you also need to use puts and not put
array.each {|i| puts i if i % 2 == 0 }
also, note that ruby has a .even? and .odd? methods you can call on integers
array.each {|i| puts i if i.odd? }
Another option is to select the even? elements and print them afterwards:
array.select(&:even?).each { |i| puts i }
Or alternatively via reject and odd?:
array.reject(&:odd?).each { |i| puts i }
The each call isn't really needed, as you can pass an entire array to puts and it will print each element on a separate line:
puts array.select(&:even?)
# or
puts array.reject(&:odd?)
All of the above will generate the same output:
2
4
6
8

How to 'puts' a number list of items in the array using an 'until' loop

I am using loops and a counter to put out a numbered list of items. It lists them all at once under 1, and then 2 is another round of listing of all of the items.
The while loop doesn't work and I figured until was better. I also moved counter outside of the first iteriation but that doesn't work either.
require 'pry'
require 'rubygems'
require 'open-uri'
require 'nokogiri'
class KefotoScraper::CLI
def initialize
#product_names = []
#page_url = "https://kefotos.mx/"
end
def call
puts "These are the services that Kefoto offers:"
list_products
end
private
def home_html
Nokogiri::HTML(open(#page_url))
end
def service_names
#service_names = home_html.css(".nav-link").map do |link|
link['href'].to_s.gsub(/.php/, "")
end
#service_names.each do |pr|
#product_names << pr
end
#product_names
end
def list_products
i = 1
n = 0
until #product_names.length < n do
#product_names.each {|list_item| puts "#{i} #{list_item}"}
i += 1
n += 1
end
end
def service_links
#service_links ||= home_html.css(".nav-item").map { |link| link['href'] }
end
end
The list repeats itself over and over again.
[3] pry(#<KefotoScraper::CLI>)> #product_names
=> ["foto-enmarcada", "impresion-fotografica", "photobooks", "impresion-directa-canvas", "impresion-acrilico", "fotoregalos"]
[4] pry(#<KefotoScraper::CLI>)> list_products
1 foto-enmarcada
1 impresion-fotografica
1 photobooks
1 impresion-directa-canvas
1 impresion-acrilico
1 fotoregalos
2 foto-enmarcada
2 impresion-fotografica
2 photobooks
2 impresion-directa-canvas
2 impresion-acrilico
2 fotoregalos
def list_products
#product_names.each_with_index do |list_item, i|
puts "#{i} #{list_item}"
end
end
edit: thanks for the feedback, the Tin Man.
With your current code, you're looping through #product_names once with until and inside of that you're looping through #product_names with .each. For example if #product_names.length == 3, you'd print 3 * 3 == 9 times!
Since you only need to loop through #product_names once, pick either until or .each. My example above uses .each and here is an example using until:
i = 1
until #product_names.length < i do
puts "#{i} #{#product_names[i-1]}"
i += 1
end

Next-ing from child each loop in Ruby

I have this bit of code:
gates.each do |key, value, temp|
unless value.class == Output
temp = value.in1
gates.each do |k, v|
if v.out == temp
value.base_distance += 1
#do a thing
end
end
end
end
What I want to happen is when the #do a thing comment inside the conditional is reached it should break out of the inner .each loop and move on to the next instance of the outer .each loop, essentially executing a next. How would I do that from inside the conditional?
TL;DR use break.
Here is the MCVE:
[1,2].each do |o|
puts "Outer: #{o}"
[1,2,3].each do |i|
break if i.even?
puts "Inner: #{i}"
end
end
#⇒ Outer: 1
# Inner: 1
# Outer: 2
# Inner: 1
FWIW, one might pass an argument to break to be returned from the block. This might be needed to emulate the next after break:
[1,2].each do |o|
puts "Outer: #{o}"
inner =
[1,2,3].each do |i|
break :next if i.even?
puts "Inner: #{i}"
end
next if inner == :next
...
end

How to correctly write a ruby method

i'm trying ruby. I want to make a program which can continue the sequence.
1
11
21
1211
111221
(which next line is a calculated each number of previous line)
For example last line is(see previous) one of 1, one of 2, and two of 1.
I make a code and it works fine:
5.times do
result_seq = []
count = 1
puts initial_seq.join
initial_seq.size.times do
if (value = initial_seq.shift) == initial_seq.first
count += 1
else
result_seq << count << value
count = 1
end
end
initial_seq = result_seq
end
But now I want to write a simple method called Next
I want to make:
sec = Sequence.new(1)
sec.next -> will return 11
sec.next.next -> will return 21
sec.next.next.next -> will return 1211
How can i write it correctly using my code?
UPD
I wrote the tests for it:
require "spec_helper"
require "sequence"
describe Sequence do
let(:sequence) { Sequence.new("1") }
describe "to_s" do
it "return initial value" do
expect(sequence.to_s).to eql "1"
end
end
describe "next" do
it "generate next state" do
expect(sequence.next.to_s).to eql "11"
end
it "return Sequence instance" do
expect(sequence.next).to be_an_instance_of(Sequence)
end
it "generate next state 2 times" do
expect(sequence.next.next.to_s).to eql "21"
end
it "generate next state 3 times" do
expect(sequence.next.next.next.to_s).to eql "1211"
end
end
end
class Sequence
attr_reader :initial_seq
def initialize(initial_seq = [])
#initial_seq = initial_seq
print_next
end
def print_next
result_seq = []
count = 1
puts initial_seq.join
initial_seq.size.times do
if (value = initial_seq.shift) == initial_seq.first
count += 1
else
result_seq << count << value
count = 1
end
end
#initial_seq = result_seq
self #<===== The most important part for being able to chain `print_next`
end
end
Usage:
Sequence.new([1]).print_next.print_next.print_next.print_next
1
11
21
1211
111221
edit
If you want to initialize it with integer argument, not array:
def initialize(number)
#initial_seq = [number]
print_next
end
Sequence.new(1).print_next.print_next
1
11
21
Or, if you do not want initialize to accept an argument (assuming, it will always start with 1):
def initialize
#initial_seq = [1]
print_next
end
Sequence.new.print_next.print_next
1
11
21
Ruby provides Enumerators, which behave almost like in OP. Leaving the original code almost unaltered:
seq = Enumerator.new do |yielder|
initial_seq = [1]
loop do #endless loop, but don't worry, its lazy
result_seq = []
count = 1
yielder << initial_seq.join
initial_seq.size.times do
if (value = initial_seq.shift) == initial_seq.first
count += 1
else
result_seq << count << value
count = 1
end
end
initial_seq = result_seq
end
end
5.times{puts seq.next}
puts seq.next

Exiting from a Kernel#loop

In Kernel#loop documentation, there is an example that uses break in order to break out of the loop. Otherwise, the documentation talks about rasing a StopIteration error.
I tried them both:
i=0
loop do
puts i
i+=1
break if i==10
end
i=0
loop do
puts i
i+=1
raise StopIteration if i==10
end
The output is the same. Are there differences between the two approaches? I think that there should be, otherwise why bother to define an error class and all the managements that come with it?
break is a keyword in ruby, which terminates the most internal loop, whether it be loop, or for, and a few more (see here).
StopIteration is an exception, which is caught by Kernel.loop (see here).
So in your scenario, they are the same, but in a different scenario, they will act differsntly:
puts "first run"
for i in 0..20 do
puts i
break if i==10
end
puts "first done"
puts "second run"
for i in 0..20 do
puts i
raise StopIteration if i==10
end
puts "second done" # <= will not be printed
Here is a scenario where StopIteration can be utilized when break cannot:
puts "first run"
def first_run(i) # will not compile
puts i
break if i==10
end
i=0
loop do
first_run(i)
i+=1
end
puts "first done"
puts "second run"
def second_run(i)
puts i
raise StopIteration if i==10
end
i=0
loop do
second_run(i)
i+=1
end
puts "second done"
Here is another use-case, which uses the fact the Enumerator.next throws a StopIteration error when the Enumerator has reached the end:
enum = 5.times.to_enum
loop do
p enum.next
end
will print
0
1
2
3
4
Thanks Cary for this example.
There are two uses of the break keyword.
First: the break keyword, when in a block, causes the method that the block was passed to to return. If you pass an argument to break, the return value of the method will be that argument. Here is an example:
def a
puts 'entering method a'
yield
puts 'leaving method a'
end
result = a { break 50 }
puts result
This will print:
entering method a
50
Second: the break keyword can cause a while, until, or for loop to terminate. Here is an example:
i = 0
result =
while i < 5
i += 1
puts i
break 75 if i == 3
end
puts result
This will print:
1
2
3
75
Your example with Kernel#loop makes use of the first case, causing the loop method to return.
StopIteration is an exception which only works with Kernel#loop as far as I can tell. For example:
infinite_loop = Enumerator.new do |y|
i = 0
while true
y << i
i += 1
end
end
infinite_loop.each do |x|
puts x
raise StopIteration if x == 4
end
Fails because StopIteration is uncaught, however:
x = 0
loop.each do
puts x
x += 1
raise StopIteration if x == 4
end
Catches StopIteration and exits.

Resources