cant understand the triagle number enumerator yielder yield - ruby

unable to understand this code how it works.
triangular_numbers = Enumerator.new do |yielder|
number = 0
count = 1
loop do
number += count
count += 1
yielder.yield number
end
end
5.times { print triangular_numbers.next, " " }
cant understand how yielder works for this block. how its yield work for number variable. how do loop runs 5 times. and how triangular_number.next works for the first time.

An enumerator is basically something that you can call next on and get something back. The yielder is the mechanism where it gives something back when next is called. Execution stops at the yield until the next call to next.
Farfetched analogy
You can think of an enumerator as a ticket machine like when you're waiting in line at a government office. When you press a button (next) it gives you a ticket. Inside the machine there's a chute where the ticket comes out. But the ticket machine is not constantly printing tickets. It waits for the button to be pressed before it prints the next ticket and puts it through the chute.
In this case the analogous code would be:
ticket_machine = Enumerator.new do |chute|
ticket = 0
loop do
#print_ticket
chute.yield ticket #waits here until you hit the button
ticket += 1
end
end
5.times { print ticket_machine.next, " " } # gets 5 tickets
Your code sample is basically the same thing, but instead of issuing tickets, it's issuing triangular numbers. The chute is the yielder where the numbers get passed through.
This is not the only way to use an enumerator, check the docs for more.

Enumerator::new accepts a block. This block, when run, receives an Enumerator::Yielder, which has a method #yield.
When the Enumerator#next is called, the block is executed, up to the first Enumerator::Yielder#yield. The execution is paused there; the value given to yield is the value that next returns. When you call next again on the same Enumerator, the execution is resumed, and proceeds until it encounters yield again.
So in your case, 5.times executes its block, intending to repeat it five times. triangular_numbers.next is called; this starts the execution of the block above. number and count are set to their values, and an infinite loop is started. number is set to 1, count is set to 2, and then we find yielder.yield. This pauses the execution of the block, and returns the control back to where next was called inside 5.times loop. next returns 1, because yielder.yield received number (1).
Second time through the 5.times loop, we want to print the next number. This stops the main execution line, and resumes the enumerator block from just after yielder.yield. The infinite loop continues; number is 3, count is 3, yielder.yield pauses the enumerator and resumes the main code. next gets 3, which gets printed.
Third, fourth and fifth time through the 5.times loop are exactly the same.
After five iterations, 5.times loop ends, and the execution proceeds past it. The enumerator is paused, ready to give the next number in sequence, if you ever call next on it again (since it has an infinite loop), but you never do, and the program exits.

I'll try to explain what it does bit by bit, so you can try to wrap your head around it.
Enumerator.new do |yielder|
end
So you instantiate an enumerator that will work over a variable called yielder.
Inside its scope you set some local vars (that will be kept as the object is reused):
number = 0
count = 1
And then you set a loop that increments number by count and count by 1 and then call yield over your argument passing number to it as an argument.
loop do
number += count
count += 1
yielder.yield number
end
5.times repeats the block passed to it 5 times. The block
-> { print triangular_numbers.next, " " }
calls print that takes n args and concatenates the parts to form a string, but does not append a newline.
The first argument is our enumerator next interaction (triangular_numbers.next), which will compute the current number and call yield on the Enumerator::Yielder that's implicitly created handling the control back to the calling Fiber along with any args that got passed to it.
(All Enumerators are implemented as "Fibers" on MRI)
So that yielder.yield call is similar to a Fiber.yield call and will allow the 5.times loop to run and return the number 1.

I'm adding a piece of code to the already clear explanation provided:
my_enum = Enumerator.new do |whatever_name_for_the_yielder|
n = 0
loop do
whatever_name_for_the_yielder.yield "Return this: #{n}"
n += 1
end
end
puts my_enum.next #=> Return this: 0
puts my_enum.next #=> Return this: 1
puts my_enum.next #=> Return this: 2
When you provide an end to the iteration, it stops with an error:
my_enum2 = Enumerator.new do |whatever_name_for_the_yielder|
2.times do |n|
whatever_name_for_the_yielder.yield "Return this: #{n}"
end
puts "Outside the loop"
end
puts my_enum2.next #=> Return this: 0
puts my_enum2.next #=> Return this: 1
puts my_enum2.next #=> Outside the loop
#=> ERROR: .....in `next': iteration reached an end (StopIteration)

Related

Can someone explain me what this line of ruby code does?

I'm a beginner in ruby and found this example on the Odin project about the reduce method, but in line 7 it puts the result variable again, can someone explain me What's the use of putting the result variable?
Thank you in advance!
votes = ["Bob's Dirty Burger Shack", "St. Mark's Bistro", "Bob's Dirty Burger Shack"]
votes.reduce(Hash.new(0)) do |result, vote|
puts "result is #{result} and votes is #{vote}"
puts "This is result [vote]: #{result[vote]}"
result[vote] += 1
result #this part I don't understand
end
They're using the reduce(initial_operand) {|memo, operand| ... } version.
memo is a thing to collect the result. The block has to pass that along to the next iteration. For example, if you wanted to sum up a list of numbers...
(1..4).inject do |sum, element|
p "Running sum: #{sum}; element: #{element}"
# This is passed along to the next iteration as sum.
sum + element
end
Instead of using the default memo, which would be the first element, they've used Hash.new(0) to count the votes. Each iteration counts the votes, and then passes the result has to the next iteration.
# result starts as Hash.new(0)
votes.reduce(Hash.new(0)) do |result, vote|
# This prints the state of the voting and the current choice being tallied.
puts "result is #{result} and votes is #{vote}"
# This displays how many votes there are for this choice at this point
# before this choice is tallied.
puts "This is result [vote]: #{result[vote]}"
# This adds the vote to the tally.
result[vote] += 1
# This passes along the tally to the next iteration.
result
end
If you don't want to print on each iteration, use tally instead.
result = votes.tally

Restarting a loop from the top

I have the following:
text_counter = 0
MAXTEXT_COUNTER = 10
puts "hello, this will start"
loop do
puts "hello"
text_counter += 1
sleep(2)
if text_counter >= MAXTEXT_COUNTER
break
end
end
sleep(7200)
print "ended test"
Once the break has happened, how can I get it to start again from the top?
I'm now thinking I could nest this loop in an until loop with the condition of text_counter == 1000. This would break, then sleep for 2 hours, then start again until it hits 1000.
It looks like you need a loop within a loop where you repeat one N times, the other M times:
MAXTEXT_COUNTER = 10
puts "hello, this will start"
loop do
MAXTEXT_COUNTER.times do
puts "hello"
sleep(2)
end
print "ended test"
sleep(7200)
end
The outer loop is perpetual. The inner one runs a certain number of times and stops using the times method.
You're looking for next
It functions similarly to break, but returns control back to the top of the loop. It's great for creating flat control flow.
For example
0.upto(100) do |i|
if i % 7 == 0
puts "#{i} is a multiple of 7"
next
end
puts i
end
There is a retry keyword which repeats the loop from top, just what you've asked.
Or you can wrap your loop into a method and continuously call that method.

For loop... Forever

I have a for loop that I would like to have increment forever.
My code:
for a in (0...Float::INFINITY).step(2)
puts a
end
Output:
0.0
2.0
4.0
Etc. Always with "#{a}.0"
Is there any way to express infinity as an integer, so that the output does not have a .0 at the end without preforming any operations on the contents of the loop?
Addendum
Could you also explain how your loop works? I am trying to find the most efficient solution, because since this loop will be iterating infinity, a few milliseconds shaved off will improve the performance greatly.
Also...
I will accept the solution that takes to shortest time to run to 1000000
According to benchmark both #Sefan and the while loop answers take the same ammount of timeFruity the while loop answers take a bit shorter, with the for loop answers in second, but the multiple loop do answers take far longer.
Since the reason why is out of the scope of this question, I have created another question that addresses why some loops are faster than others (https://stackoverflow.com/questions/33088764/peddle-to-the-metal-faster-loop-faster).
You can use Numeric#step without passing a limit:
0.step(by: 2) { |i| puts i }
Output:
0
2
4
6
...
You can also build your own Enumerator:
step2 = Enumerator.new do |y|
a = 0
loop do
y << a
a += 2
end
end
step2.each { |i| puts i }
You can use while true for that:
puts a = 0
puts a+=2 while true
BTW,
Is there any way to express infinity as an integer
NO
require 'bigdecimal'
(0..BigDecimal('Infinity')).step(2).each{ |n| puts n }
OR
require 'bigdecimal'
for a in (0...BigDecimal::INFINITY).step(2)
puts a
end
This is what the loop method is designed for. loop has no condition for which to run. It will run indefinitely and the only way to exit is to use the keyword break. (or raise a StopIteration)
a = 0
loop { puts a += 2}
This loop will be infinite as there is no break specified.
break can be specified very similarly to how the other answers use the while condition if needed:
a = 0
loop do
puts a += 2
break if a > 1_000_000
end
This loop will now exit once the value of a exceeds 1M.
That being said #Stefan's answer is more efficient as it does not store this integral value or have to perform any additional assignment but rather the number is simply yielded from an Enumerator and discarded it afterwards. The usefulness of this becomes more a matter of your implementation and purpose for this loop.
Try this:
arr = [0]
arr.cycle(1000000) { |i| puts arr[0] +=2 }
If you want infinite loop, then, don't pass any parameter to cycle
arr = [0]
arr.cycle { |i| puts arr[0] +=2 }
a = [-2]
puts a.unshift(a.shift+2) while 'loop forever'

Ruby retry Array#each loop

I have an array of lambda's i would like to run all of the lamba's at the same time, the array can have any number of lamba's this makes it hard. Since you can only enumerate through an array.
def detect_int(*args)
return 1 if args.empty?
n = 1
args.each do |lam|
until lam.call(n) do
n += 1
end
retry if lam.call(n) == false
end
n
end
This method should work in theory, it should increase the count till lam1.call(n) returns true. then it will move to the next lam, if lam2.call(n) returns false it should retry the args.each loop with the incremented n starting at lam1 again. and so on.
Only i am getting an SyntaxError: (irb):76: Invalid retry how would you retry that each loop so that it will start it from the beginning
I was reading up on ruby loops and the retry method here. mine seems to fit the syntax correctly, but because its a loop within a loop it can get confusing. currently its sitting in the args loop which is the loop i would like to reset.
Since retry is a ruby keyword, parser wants to see either a native loop or a begin-rescue-end condition around. There is no one and the parser got stuck (Array#each is not a ruby loop, it’s an invocation of plain ruby method on array instance.) The possible solution would be:
args.each do |lam|
begin # grant parser with a scope
until lam.call(n) do
n += 1
end
raise if lam.call(n) == false
rescue
retry
end
end
Here is another way of doing it. Once every instance of args is true it will only break the loop. this is dangerous because it could never satisfy the loop and go on indefinitely
n = 1
while true do
args.all? { |lam| lam.call(n) } and return n
n += 1
end

Ruby Variable Reference Issue

I am not fluent in ruby and am having trouble with the following code example. I want to pass the array index to the thread function. When I run this code, all threads print "4". They should instead print "0 1 2 3 4" (in any order).
It seems that the num variable is being shared between all iterations of the loop and passes a reference to the "test" function. The loop finishes before the threads start and num is left equal to 4.
What is going on and how do I get the correct behavior?
NUM_THREADS = 5
def test(num)
puts num.to_s()
end
threads = Array.new(NUM_THREADS)
for i in 0..(NUM_THREADS - 1)
num = i
threads[i] = Thread.new{test(num)}
end
for i in 0..(NUM_THREADS - 1)
threads[i].join
end
Your script does what I would expect in Unix but not in Windows, most likely because the thread instantiation is competing with the for loop for using the num value. I think the reason is that the for loop does not create a closure, so after finishing that loop num is equal to 4:
for i in 0..4
end
puts i
# => 4
To fix it (and write more idiomatic Ruby), you could write something like this:
NUM_THREADS = 5
def test(num)
puts num # to_s is unnecessary
end
# Create an array for each thread that runs test on each index
threads = NUM_THREADS.times.map { |i| Thread.new { test i } }
# Call the join method on each thread
threads.each(&:join)
where i would be local to the map block.
"What is going on?" => The scope of num is the main environment, so it is shared by all threads (The only thing surrounding it is the for keyword, which does not create a scope). The execution of puts in all threads was later than the for loop on i incrementing it to 4. A variable passed to a thread as an argument (such as num below) becomes a block argument, and will not be shared outside of the thread.
NUM_THREADS = 5
threads = Array.new(NUM_THREADS){|i| Thread.new(i){|num| puts num}}.each(&:join)

Resources