I want to create an enumerator for external iteration via next that is clone-able, so that the clone retains the current enumeration state.
As an example, let's say I have a method that returns an enumerator which yields square numbers:
def square_numbers
return enum_for(__method__) unless block_given?
n = d = 1
loop do
yield n
d += 2
n += d
end
end
square_numbers.take(10)
#=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
And I want to enumerate the first 5 square numbers, and for each value, print the subsequent 3 square numbers. Something that's trivial with each_cons:
square_numbers.take(8).each_cons(4) do |a, *rest|
printf("%2d: %2d %2d %2d\n", a, *rest)
end
Output:
1: 4 9 16
4: 9 16 25
9: 16 25 36
16: 25 36 49
25: 36 49 64
But unlike the above, I want to use external iteration using two nested loops along with next and clone:
outer_enum = square_numbers
5.times do
i = outer_enum.next
printf('%2d:', i)
inner_enum = outer_enum.clone
3.times do
j = inner_enum.next
printf(' %2d', j)
end
print("\n")
end
Unfortunately, the above attempt to clone raises a:
`initialize_copy': can't copy execution context (TypeError)
I understand that Ruby doesn't provide this out-of-the-box. But how can I implement it myself? How can I create an Enumerator that supports clone?
I assume that it's a matter of implementing initialize_copy and copying the two variable values for n and d, but I don't know how or where to do it.
Ruby fibers cannot be copied, and the C implementation of Enumerator stores a pointer to a fiber which does not appear to be exposed to Ruby code in any way.
https://github.com/ruby/ruby/blob/752041ca11c7e08dd14b8efe063df06114a9660f/enumerator.c#L505
if (ptr0->fib) {
/* Fibers cannot be copied */
rb_raise(rb_eTypeError, "can't copy execution context");
}
Looking through the C source, it's apparent that Enumerators and Fibers are connected in a pretty profound way. So I doubt that there is any way to change the behavior of initialize_copy to permit clone.
Perhaps you could just write a class of your own that does what you ask:
class NumberSquarer
def initialize
#n = #d = 1
end
def next
ret = #n
#d += 2
#n += #d
ret
end
end
ns1 = NumberSquarer.new
Array.new(5) { ns1.next }
# => [1, 4, 9, 16, 25]
ns2 = ns1.clone
Array.new(5) { ns2.next }
# => [36, 49, 64, 81, 100]
Disclaimer: I'm answering my own question
One way to achieve this is by sub-classing Enumerator. In particular, the now-deprecated variant that takes an object and a method:
class ObjectEnumerator < Enumerator
attr_reader :object, :method
def initialize(object, method = :each)
#object = object
#method = method
super
end
def initialize_copy(orig)
initialize(orig.object.clone, orig.method)
end
end
That orig.object.clone above is where the magic happens: it clones the object we are traversing.
In addition, we need such clone-able object. A simple way is to have a class which holds the state as instance variables: (shamelessly copied from Kache's answer)
class NumberSquarer
def initialize
#d = -1
#n = 0
end
def each
return ObjectEnumerator.new(self, __method__) unless block_given?
loop do
#d += 2
#n += #d # had to be reordered b/c
yield #n # <- yield has to come last
end
end
end
This gives us a basic, clone-able enumerator:
e = NumberSquarer.new.each
#=> #<ObjectEnumerator: #<NumberSquarer:0x00007fde60915e10 #d=-1, #n=0>:each>
e.next #=> 1
e.next #=> 4
other = enum.clone
#=> #<ObjectEnumerator: #<NumberSquarer:0x00007fcf23842520 #d=3, #n=4>:each>
enum.next #=> 9
enum.next #=> 16
other.next #=> 9
I'm providing a different solution that is not a straight answer to the question:
How can I create an Enumerator that supports clone?
But if I'm not wrong the only purpose of cloning the not clonable Ruby's Enumerator is to get a reference to the next object in the enumerator.
In this case, we need both values stored in odd_sum and square in the example below.
We can store those values in an Array and return the array instead of a single value, then we can use Enumerator.peek in order to have the array that is used to initialize a new Enumerator.
def square_numbers(starters = {})
return enum_for(__method__, starters) unless block_given?
last_odd = starters.fetch(:square_odd, [1,1])[1]
square = starters.fetch(:square_odd, [1,1])[0]
loop do
yield [square, last_odd]
last_odd += 2
square += last_odd
end
end
outer_enum = square_numbers
5.times do
i = outer_enum.next[0]
printf('%2d:', i)
inner_enum = square_numbers(square_odd: outer_enum.peek)
3.times do
j = inner_enum.next[0]
printf(' %2d', j)
end
print("\n")
end
Related
I am new to the ruby and was practicing a code. I want to count the letters in a string by a self written code, without using #length or #size method. I have searched online but am unable to find anything relating to my query. I would appreciate if anyone could help me out in this simple program.
Other option, mapping String#chars with index then picking the last:
str = "123456"
str.chars.map.with_index { |_, i| i + 1 }.last
#=> 6
It generates an Array, but we are not looking for efficiency here.
Or even using String#index with offset:
str = "aaaa"
str.index(str[-1], -1) + 1
#=> 4
It looks for the index of the latest char starting from the end.
You can do that using any String method that enumerates characters. The most obvious is String#each_char, as #knut mentioned in a comment.
def str_length(str)
enum = str.each_char
n = 0
loop do
enum.next
n += 1
end
n
end
str_length "Zaphod"
#=> 6
Let's see what is happening here.
str = "Zaphod"
enum = str.each_char
#=> #<Enumerator: "123456":each_char>
n = 0
loop do
s = enum.next
n += 1
puts "s = #{s}, n = #{n}"
end
n #=> 6
prints
s = Z, n = 1
s = a, n = 2
s = p, n = 3
s = h, n = 4
s = o, n = 5
s = d, n = 6
See Enumerator#next. After enum.next #=> "d" is executed enum.next is executed once more, raising a StopIteration exception. That exception is handled by Kernel#loop by breaking out of the loop.
As I said at the outset, any String method could be used that enumerates characters. For example, enum = str.gsub(/./).
The same approach could be used for any class that implements a method that enumerates elements of a collection. For example, we could add a method to the Enumerable module, which would then be available for every class that includes that module.
module Enumerable
def my_length
enum = each
n = 0
loop do
enum.next
n += 1
end
n
end
end
[1,2,3,4].my_length
#=> 4
{ a: 1, b: 2 }.my_length
#=> 2
(1..5).my_length
#=> 5
I am trying to write a method that asks for two 2 integers, and divides the first by the second and returns the result including the remainder.
def remainder(a,b)
return a/b
return a%b
end
puts remainder(100,6)
This puts out
16
If I use this code
def remainder(a,b)
return a%b
end
puts remainder(100,6)
This puts out
4
I don't understand how to make both the modulus value and the remainder show in puts statement.
Update
Based on Simple Lime's advice I used the following code...
def remainder(a,b)
return a.divmod(b)
end
puts remainder(100,6)
Which puts
16
4
And is functioning as I had hoped.
You can return an array from the method when you need to return multiple values:
def remainder(a, b)
[a / b, a % b]
end
puts remainder(100, 6).inspect # => [16, 4]
and then you can assign each value to a different variable, if you need:
div, mod = remainder(100, 6)
puts div # => 16
puts mod # => 4
As a side note, if you are just needing both the quotient and modulus of 2 numbers, there's already a built-in function, divmod that does this, using the technique above:
100.divmod(6) # => [16, 4]
I was attempting to solve Project Euler #58 in a functional manner with ruby.
Briefly, I created an enumerator to return the corner number of each ring. I was then chaining functional operators on the enumerator. When I get my result, I find that it has a different class depending on how I use it.
spiral = Enumerator.new do |yielder|
n = 3
step = 2
loop do
vals = n.step(nil, step).take(4)
yielder.yield vals
step += 2
n = vals.last + step
end
end
primes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113]
levels = spiral
.lazy
.map { |ring| ring.count { |n| primes.include? n } }
.with_object({:total=>1.0, :primes=>0})
.take_while do |ring_primes, counts|
counts[:total] += 4
counts[:primes] += ring_primes
(counts[:primes] / counts[:total]) > 0.5
end
The class of levels is a lazy enumerator. I would expect it to contain the number of primes in each ring [3, 2, 3, etc.] - see the project euler reference.
If I just print from the enumerator, I get what I expect:
levels.each do |level|
puts "#{level}"
end
Returns:
3
2
3
1
But if I loop .with_index I get an array result back where the expected value is the first member and the second is my .with_object parameter
levels.each.with_index do |level, ix|
puts "#{ix}: #{level}"
end
Returns:
0: [3, {:total=>5.0, :primes=>3}]
1: [2, {:total=>9.0, :primes=>5}]
2: [3, {:total=>13.0, :primes=>8}]
3: [1, {:total=>17.0, :primes=>9}]
Why does the lazy enumerator work this way and how could I predict for it in the future?
Update
I asked around on the IRC ruby channel and no one there had any idea about it. They said they had discussed it a day or two ago and hadn't come to any conclusions.
In general, it seems one must just deal with it and move on.
What's happening here is you're conveniently ignoring the structure that's returned and plucking out the first item to display. In this case the first item is the counts structure you produce.
Have a look at this:
levels.each do |*level|
puts level.inspect
end
That shows you what's actually in the levels results. When Ruby calls a lambda it will discard any additional data that doesn't fit with the number of arguments the block accepts.
If you don't need that metadata, strip it out:
levels = spiral
.lazy
.map { |ring| ring.count { |n| primes.include? n } }
.with_object({:total=>1.0, :primes=>0})
.take_while do |ring_primes, counts|
counts[:total] += 4
counts[:primes] += ring_primes
(counts[:primes] / counts[:total]) > 0.5
end
.map { |r,_| r }
That removes the extraneous element in the results.
Here's a way of cleaning up your Enumerator a bit:
class Spiral
include Enumerable
def each
Enumerator.new do |yielder|
n = 3
step = 2
loop do
vals = n.step(nil, step).take(4)
yielder.yield vals
step += 2
n = vals.last + step
end
end
end
end
Then you can create one with:
Spiral.new.each ...
I'm making a Yahtzee game in Ruby using Shoes
when I click Button "Two" the code is suppose to count the amount of times the
value 2 occurs in an array. For every instance of the value 2 that appears,
the score is incremented by 2.
This code works for a select amount of cases but on other cases like
#array = [2,1,2,2,3] # there are three 2's in the array so
the score is suppose to be 6, but instead my code returns 4... why?
button " twos " do
#array.each_with_index do |value, index|
if (#array[index] == 2)
#score = #score + 2
#points = #score + 2
end #if
end #loop end #button
This code looking better, but, in fact, it does the same thing. Maybe you should check initial values of instance variables #score and #points?
#array = [2,1,2,2,3]
#score = #points = 0
#score = #array.count(2) * 2
#points = #score
#score
=> 6
#points
=> 6
I recommend you to use Enumerable#inject method. By means of inject you can implement abstract method for counting numbers and use it everywhere in your project:
def count_of_element array, element
array.inject(0) { |count, e| count += 1 if e == element; count }
end
puts count_of_element [2,1,2,2,3], 2 # => 3
There could be even better solution – define method for Array class like this:
class Array
def count_of_element element
inject(0) { |count, e| count += 1 if e == element; count }
end
end
puts [2,1,2,2,3].count_of_element 2 # => 3
It looks even cooler. Good luck!
Whats the best way in Ruby to do something like my_array.select(n){ |elem| ... }, where the n means "I only want n elements returned, and stop evaluating after that number is reached"?
This should do the trick:
my_array.select(n) { |elem| elem.meets_condition? }.take(n)
However, this will still evaluate all items.
If you have a lazy enumerator, you could do this in a more efficient manner.
https://github.com/ruby/ruby/pull/100 shows an attempt at enabling this feature.
You can easily implement lazy_select:
module Enumerable
def lazy_select
Enumerator.new do |yielder|
each do |e|
yielder.yield(e) if yield(e)
end
end
end
end
Then things like
(1..10000000000).to_enum.lazy_select{|e| e % 3 == 0}.take(3)
# => [3, 6, 9]
execute instantly.
Looks like there's no avoiding a traditional loop if you're using stock 1.8.7 or 1.9.2...
result = []
num_want = 4
i = 0
while (elem = my_array[i]) && my_array.length < num_want
result << elem if elem.some_condition
i += 1
end
You could make an Enumerable-like extension which has your desired selectn semantics:
module SelectN
def selectn(n)
out = []
each do |e|
break if n <= 0
if yield e
out << e
n -= 1
end
end
out
end
end
a = (0..9).to_a
a.select{ |e| e%3 == 0 } # [0, 3, 6, 9]
a.extend SelectN
a.selectn(1) { |e| e%3 == 0 } # [0]
a.selectn(3) { |e| e%3 == 0 } # [0, 3, 6]
# for convenience, you could inject this behavior into all Arrays
# the usual caveats about monkey-patching std library behavior applies
class Array; include SelectN; end
(0..9).to_a.selectn(2) { |e| e%3 == 0 } # [0,3]
(0..9).to_a.selectn(99) { |e| e%3 == 0 } # [0,3, 6, 9]
Why not flip this around and do the #take before the #select:
my_array.take(n).select { |elem| ... }
That will ensure you only do your computation for n number of items.
EDIT:
Enumerable::Lazy is known to be slower, but if your computation is known to be more computationally expensive than the lazy slowness, you can use the Ruby 2.0 feature:
my_array.lazy.select { |elem| ... }.take(n)
See: http://blog.railsware.com/2012/03/13/ruby-2-0-enumerablelazy/
I guess broken loop can be done in old-fashioned loop style with break or something like this:
n = 5
[1,2,3,4,5,6,7].take_while { |e| n -= 1; n >= 0 && e < 7 }
In functional language this would be recursion, but without TCO it doesn't make much sense in Ruby.
UPDATE
take_while was stupid idea as dbenhur pointed out, so I don't know anything better than a loop.