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)
Related
I want to:
pass a block to a method call, and then
pass that entire method call as the condition of a while loop,
even though I don't need to put any logic inside the loop itself.
Specifically, I have an array that I'd like to #reject! certain elements from based on rather complicated logic. Subsequent calls to #reject! may remove elements that were not removed on a previous pass. When #reject! finally stops finding elements to reject, it will return nil. At this point, I would like the loop to stop and the program to proceed.
I thought I could do the following:
while array.reject! do |element|
...
end
end
I haven't actually tried it yet, but this construction throws vim's ruby syntax highlighter for a loop (i.e., it thinks the first do is for the while statement, and thinks the second end is actually the end of the encapsulating method). I also tried rewriting this as an inline while modifier attached to a begin...end block,
begin; end while array.reject! do |element|
...
end
but it still screws up the highlighting in the same way. In any case, it feels like an abuse of the while loop.
The only way I could think of to accomplish this is by assigning the method call as a proc:
proc = Proc.new do
array.reject! do |element|
...
end
end
while proc.call do; end
which works but feels kludgy, especially with the trailing do; end.
Is there any elegant way to accomplish this??
It's not just vim, while array.reject! do |element| is invalid syntax:
$ ruby -c -e 'while array.reject! do |element| end'
-e:1: syntax error, unexpected '|'
while array.reject! do |element| end
^
You could use { ... } instead of do ... end:
while array.reject! { |element|
# ...
}
end
or loop and break:
loop do
break unless array.reject! do |element|
# ...
end
end
a little more explicit:
loop do
r = array.reject! do |element|
# ...
end
break unless r
end
Ruby lets you move your condition to the end of the loop statement. This makes it easy to store a result inside of the loop and check it against the conditional:
begin
any_rejected = arr.reject! { … }
end while any_rejected
This would work the same as doing end while arr.reject! { … }, but it's much clearer here what's happening, especially with a complicated reject!.
You're right that the Ruby parser thinks that do belongs to while, and doesn't understand where the second end is coming from. It's a precedence problem.
This code is just to show that it can be done. For how it should be done, see Stefan's answer :
array = (1..1000).to_a
while (array.reject! do |element|
rand < 0.5
end)
p array.size
end
It outputs :
473
238
113
47
30
18
8
1
0
My personal preference in situations where I need to call a method until the return value is what I want is:
:keep_going while my_method
Or more tersely I sometimes use:
:go while my_method
It's one line, and you can use the contents of the symbol to help document what's going on. With your block, I'd personally create a proc/lambda out of it and pass that to reject for clarity.
# Harder to follow, IMHO
:keep_going while array.reject! do |...|
more_code
end
# Easier to follow, IMHO
simplify = ->(...){ ... }
:keep_simplifying while array.reject!(&simplify)
This simple example I run on jruby, but it only one thread runs
require 'benchmark'
require 'celluloid/current'
TIMES = 10
def delay
sleep 1
# 40_000_000.times.each{|i| i*i}
end
p 'celluloid: true multithreading?'
class FileWorker
include Celluloid
def create_file(id)
delay
p "Done!"
File.open("out_#{id}.txt", 'w') {|f| f.write(Time.now) }
end
end
workers_pool = FileWorker.pool(size: 10)
TIMES.times do |i|
# workers_pool.async.create_file(i) # also not happens
future = Celluloid::Future.new { FileWorker.new.create_file(i) }
p future.value
end
All created files have interval 1 second.
Please help to turn Celluloid into multithreading mode, where all files are created simultaneously.
Thanks!
FIXED:
Indeed, array of "futures" helps!
futures = []
TIMES.times do |i|
futures << Celluloid::Future.new { FileWorker.new.create_file(i) }
end
futures.each {|f| p f.value }
Thanks jrochkind !
Ah, I think I see.
Inside your loop, you are waiting for each future to complete, at the end of the loop -- which means you are waiting for one future to complete, before creating the next one.
TIMES.times do |i|
# workers_pool.async.create_file(i) # also not happens
future = Celluloid::Future.new { FileWorker.new.create_file(i) }
p future.value
end
Try changing it to this:
futures = []
TIMES.times do |i|
futures << Celluloid::Future.new { FileWorker.new.create_file(i) }
end
futures.each {|f| p f.value }
In your version, consider the first iteration the loop -- you create a future, then call future.value which waits for the future to complete. The future.value statement won't return until the future completes, and the loop iteration won't finish and loop again to create another future until the statement returns. So you've effectively made it synchronous, by waiting on each future with value before creating the next.
Make sense?
Also, for short code blocks like this, it's way easier on potential SO answerers if you put the code directly in the question, properly indented to format as code, instead of linking out.
In general, if you are using a fairly widely used library like Celluloid, and finding it doesn't seem to do the main thing it's supposed to do -- the first guess should probably be a bug in your code, not that the library fundamentally doesn't work at all (someone else would have noticed before now!). A question title reflecting that, even just "Why doesn't my Celluloid code appear to work multi-threaded" might have gotten more favorable attention than a title suggesting Celluloid fundamentally does not work -- without any code in the question itself demonstrating!
This little script is supposed to generate a user-specified amount of random numbers and print them. It's a multithreaded script and I think that's where my trouble lies. I'm not getting any errors, but when run the script just hangs.
num = []
while 0.upto ARGV[0].to_i do
num << rand{254}
end
current_index = 0
while current_index < num.size
chunk = num[current_index, 5]
threads = []
chunk.each do |n|
threads << Thread.new do
puts n
end
end
threads.each do |thread|
thread.join
end
current_index += chunk.size
end
You cannot use while loop with upto.
Change it to:
0.upto ARGV[0].to_i do
num << rand(254)
end
And it works properly (I've changed braces to curly one, because I believe you want 254 to be parameter here).
Sidenote:
Remember when writing threads program in Ruby, that CRuby has GIL - Global Interpreter Lock. Therefore only one thread will be operating at one time. If you want different behaviour - switch for example to jRuby. More information about GIL can be found f.e. here: http://www.jstorimer.com/blogs/workingwithcode/8085491-nobody-understands-the-gil
upto returns self, which is a number. Everything which isn't false or nil is trueish in Ruby, including numbers. So, you have a while loop whose condition is always trueish, ergo, will never stop.
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
In Perl, there is an ability to break an outer cycle like this:
AAA: for my $stuff (#otherstuff) {
for my $foo (#bar) {
last AAA if (somethingbad());
}
}
(syntax may be wrong), which uses a loop label to break the outer loop from inside the inner loop. Is there anything similar in Ruby?
Consider throw/catch. Normally the outside loop in the below code will run five times, but with throw you can change it to whatever you like, breaking it in the process. Consider this perfectly valid ruby code:
catch (:done) do
5.times { |i|
5.times { |j|
puts "#{i} #{j}"
throw :done if i + j > 5
}
}
end
What you want is non-local control-flow, which Ruby has several options for doing:
Continuations,
Exceptions, and
throw/catch
Continuations
Pros:
Continuations are the standard mechanism for non-local control-flow. In fact, you can build any non-local control-flow (subroutines, procedures, functions, methods, coroutines, state machines, generators, conditions, exceptions) on top of them: they are pretty much the nicer twin of GOTO.
Cons:
Continuations are not a mandatory part of the Ruby Language Specification, which means that some implementations (XRuby, JRuby, Ruby.NET, IronRuby) don't implement them. So, you can't rely on them.
Exceptions
Pros:
There is a paper that proves mathematically that Exceptions can be more powerful than Continuations. IOW: they can do everything that continuations can do, and more, so you can use them as a replacement for continuations.
Exceptions are universally available.
Cons:
They are called "exceptions" which makes people think that they are "only for exceptional circumstances". This means three things: somebody reading your code might not understand it, the implementation might not be optimized for it (and, yes, exceptions are godawful slow in almost any Ruby implementation) and worst of all, you will get sick of all those people constantly, mindlessly babbling "exceptions are only for exceptional circumstances", as soon as they glance at your code. (Of course, they won't even try to understand what you are doing.)
throw/catch
This is (roughly) what it would look like:
catch :aaa do
stuff.each do |otherstuff|
foo.each do |bar|
throw :aaa if somethingbad
end
end
end
Pros:
The same as exceptions.
In Ruby 1.9, using exceptions for control-flow is actually part of the language specification! Loops, enumerators, iterators and such all use a StopIteration exception for termination.
Cons:
The Ruby community hates them even more than using exceptions for control-flow.
No, there isn't.
Your options are:
put the loop in a method and use return to break from the outer loop
set or return a flag from the inner loop and then check that flag in the outer loop and break from it when the flag is set (which is kind of cumbersome)
use throw/catch to break out of the loop
while c1
while c2
do_break=true
end
next if do_break
end
or "break if do_break" depending on what you want
Perhaps this is what you want? (not tested)
stuff.find do |otherstuff|
foo.find do
somethingbad() && AAA
end
end
The find method keeps looping until the block returns a non null value or the end of the list is hit.
Wrapping an internal method around the loops could do the trick
Example:
test = [1,2,3]
test.each do |num|
def internalHelper
for i in 0..3
for j in 0..3
puts "this should happen only 3 times"
if true
return
end
end
end
end
internalHelper
end
Here you can do a check inside any of the for loops and return from the internal method once a condition is met.
I know I will regret this in the morning but simply using a while loop could do the trick.
x=0
until x==10
x+=1
y=0
until y==10
y+=1
if y==5 && x==3
x,y=10,10
end
end
break if x==10
puts x
end
The if y==5 && x==3 is only an example of an expression turning true.
You may consider adding a flag, which is set inside the inner loop, for controlling the outer loop.
'next' the outer loop
for i in (1 .. 5)
next_outer_loop = false
for j in (1 .. 5)
if j > i
next_outer_loop = true if j % 2 == 0
break
end
puts "i: #{i}, j: #{j}"
end
print "i: #{i} "
if next_outer_loop
puts "with 'next'"
next
end
puts "withOUT 'next'"
end
'break' the outer loop
for i in (1 .. 5)
break_outer_loop = false
for j in (1 .. 5)
if j > i
break_outer_loop = true if i > 3
break
end
puts "i: #{i}, j: #{j}"
end
break if break_outer_loop
puts "i: #{i}"
end