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, " " }
puts
I know you all have answers questions about this before.
I am trying to understand more about what going on
Am I right to say yielder is a parameter which is probably a hash or an array and
yielder.yield number is basically pushing whatever number it is on to that array.
Also I seen people use yielder << number, i assume that you can also use
yielder.push(number), will it do the same thing.
One other thing I like to know why the number is retaining its value.
No it's not a data structure. It's an object - an instance of Enumerator::Yielder which, if you click the link, you will see isn't documented very well! It is written in C, and the only methods it has are yield and the alias <<. You should leave it to the Ruby core to handle itself.
It's essentially like a pipe, which is used internally by the Enumerator and Enumerable methods to fetch items from an enumeration as required. Enumerator#next, for example, will fetch the next item from the sequence. The methods available in Enumerable are much more comprehensive, and are based on the basic operations provided by Enumerator.
Related
I'm working on a mini project for a summer class. I'd like some feedback on the code I have written, especially part 3.
Here's the question:
Create an array called numbers containing the integers 1 - 10 and assign it to a variable.
Create an empty array called even_numbers.
Create a method that iterates over the array. Place all even numbers in the array even_numbers.
Print the array even_numbers.
Here's my code, so far:
numbers = [1,2,3,4,5,6,7,8,9,10]
print numbers[3]
even_numbers.empty?
def even_numbers
numbers.sort!
end
Rather than doing explicit iteration, the best way is likely Array#select thus:
even_numbers = numbers.select { |n| n.even? }
which will run the block given on each element in the array numbers and produce an array containing all elements for which the block returned true.
or an alternative solution following the convention of your problem:
def get_even_numbers(array)
even_num = []
array.each do |n|
even_num << n if n.even?
end
even_num
end
and of course going for the select method is always preferred.
I can only find methods that look for specific elements of an array.
During my objects.each |a| loop, I want to know when I'm at the final element so I can have a loop like:
objects.each |a|
if objects.hasNext()
puts a.name + ","
else
puts a.name
Iterator's hasNext() determines if the Array's iterator has another element after the one currently being evaluated.
I want to emphasize that I'm looking to print out these values, not turn them in to an Array. .join is not what I'm looking for here.
No, there isn't. However, note that hasNext is not an Array operation in Java, either. It's an Iterator operation, and Ruby's equivalent to Java's Iterator is Enumerator, not Array.
However, Ruby's Enumerator works a little bit different than Java's: instead of asking whether there is a next element, and then advancing the iterator, you simply try to look at the next element and it throws a StopIteration exception when there are no more elements.
So, the equivalent to Java's
iter.hasNext);
would be roughly
begin
enum.peek
true
rescue StopIteration
false
end
However, you almost never iterate manually in Ruby. Instead, you use higher-level iteration methods such as join, flat_map, group_by, uniq, sort, sort_by, inject, map, each_with_object, each etc.
For example:
%w(pretty ugly stupid).join(', ') # => 'pretty, ugly, stupid'
Is there a comparable Array operation to Java's hasNext() in ruby?
First of all Java's Array doesn't have any hasNext method per se because it wouldn't make any sense. It's the iterator that has it. In Ruby there's no such a thing as a list and the powerful iterator methods (each and the each_* family) would make it pretty useless:
my_array.each do |current|
// operations
// implicit:
// if (current.has_next) current = current.next
// else break
end
So, no there's no such a thing.
I'm using .each |a| to run the loop. I want to print a comma each time through unless it's the last. I want this list (pretty, ugly, stupid) not (pretty, ugly, stupid,). Any thoughts
You should take a look at the .join method.
It is common that, when leaning a new language, people tend to looking for something that familiar with:)
Your specific questions could easily be solved by using each_with_index.
objects.each_with_index do |object ,index|
if index == (object.length -1) then
puts a.name + ","
else
puts a.name
end
end
In the Ruby library iterators are implemented as internal iterators in contrast to Java which implements external iterators. The key difference between the two is that the former are designed to not let the client control the iteration, while the latter leave to the client this responsibility.
The purpose of a method like hasNext is to control iteration directly, thus in Ruby no such thing exists. Methods like peek and next defined by Enumerator are, I guess, not intended to control iteration directly but to implement custom internal iterators.
That said, your problem is easily solved with this code:
puts objects.map(&:name).join(', ')
However sometimes could be useful to concoct your own internal iterator using an Enumerator object:
module Enumerable
def puts_each_with_separator(separator)
enum = each
loop do
print yield(enum.next)
enum.peek rescue break
print separator
end
puts
end
end
objects.puts_each_with_separator(', ', &:name)
I want to emphasize that I'm looking to print out these values, not
turn them in to an Array. .join is not what I'm looking for here.
Actually, I think .join is exactly what you're looking for. The result of .join is a string, not an array, so
puts objects.join(",")
does what you say you want.
I'm doing a SaaS course with Ruby. On an exercise, I'm asked to calculate the cartesian product of two sequences by using iterators, blocks and yield.
I ended up with this, by pure guess-and-error, and it seems to work. But I'm not sure about how. I seem to understand the basic blocks and yield usage, but this? Not at all.
class CartProd
include Enumerable
def initialize(a,b)
#a = a
#b = b
end
def each
#a.each{|ae|
#b.each{|be|
yield [ae,be]
}
}
end
end
Some explanation for a noob like me, please?
(PS: I changed the required class name to CartProd so people doing the course can't find the response by googling it so easily)
Let's build this up step-by-step. We will simplify things a bit by taking it out of the class context.
For this example it is intuitive to think of an iterator as being a more-powerful replacement for a traditional for-loop.
So first here's a for-loop version:
seq1 = (0..2)
seq2 = (0..2)
for x in seq1
for y in seq2
p [x,y] # shorthand for puts [x, y].inspect
end
end
Now let's replace that with more Ruby-idiomatic iterator style, explicitly supplying blocks to be executed (i.e., the do...end blocks):
seq1.each do |x|
seq2.each do |y|
p [x,y]
end
end
So far, so good, you've printed out your cartesian product. Now your assignment asks you to use yield as well. The point of yield is to "yield execution", i.e., pass control to another block of code temporarily (optionally passing one or more arguments).
So, although it's not really necessary for this toy example, instead of directly printing the value like above, you can yield the value, and let the caller supply a block that accepts that value and prints it instead.
That could look like this:
def prod(seq1, seq2)
seq1.each do |x|
seq2.each do |y|
yield [x,y]
end
end
end
Callable like this:
prod (1..2), (1..2) do |prod| p prod end
The yield supplies the product for each run of the inner loop, and the yielded value is printed by the block supplied by the caller.
What exactly do you not understand here? You've made an iterator that yields all possible pairs of elements. If you pass CartProd#each a block, it will be executed a.length*b.length times. It's like having two different for cycles folded one into another in any other programming language.
yield simply passes (yields) control to a block of code that has been passed in as part of the method call. The values after the yield keyword are passed into the block as arguments. Once the block has finished execution it passes back control.
So, in your example you could call #each like this:
CartProd.new([1, 2], [3, 4]).each do |pair|
# control is yielded to this block
p pair
# control is returned at end of block
end
This would output each pair of values.
Consider a simple Enumerator like this:
natural_numbers = Enumerator.new do |yielder|
number = 1
loop do
yielder.yield number
number += 1
end
end
My question is: Why does ruby require that we invoke yield on the yielder object? Said another way: Why can't we replace yielder.yield number with yield number? In this example, it would be appear to be the same thing, if it were allowed. Are there examples where yielder is used in a nontrivial way? If so, can you give one? If not, what is the purpose of yielder?
Thanks.
Not 100% sure if that's the reason, but yield alone (always) applies to the block submitted to the method which calls yield: in your case the method which contains natural_numbers assignment; and it's not possible for it to perform what is desired for Enumerator, i.e. to emit the Enumerator element. Although bearing the same name, Yielder#yield is a method, and Ruby's yield is a statement.
In other words, it would not be possible to implement Enumerator constructor which would work with yield statement.
I'm implementing a loop in Ruby, but it looks ugly and I wonder if there's a neater, more Ruby-like way of writing it:
def get_all_items
items = []; page = 1; page_items = nil
while page_items != [] # Loop runs until no more items are received
items += (page_items = get_page_items(page))
page += 1
end
items
end
Note that the get_page_items method runs a HTTP request to get the items for the page, and there is no way of knowing the number of pages, or the total number of items, or the number of items for any page before actually executing the requests in order until one of them returns an empty item set.
Imagine leafing through a catalog and writing down all the products, without knowing in advance how many pages it has, or how many products there are.
I think that this particular problem is compounded because A) there's no API for getting the total number of items and B) the response from get_page_items is always truthy. Further, it doesn't make sense for you to iteratively call a method that is surely making individual requests to your DB with an arbitrary limit, only to concatenate them together. You should, at the risk of repeating yourself, implement this method to prompt a DB query (i.e. model.all).
Normally when you are defining an empty collection, iterating and transforming a set, and then returning a result, you should be using reduce (a.k.a inject):
array.reduce(0) { |result, item| result + item } # a quick sum
Your need to do a form of streaming in this same process makes this difficult without tapping into Enumerable. I find this to be a good compromise that is much more readable, even if a bit distasteful in fondling this items variable too much:
items = []
begin
items << page_items = get_page_items(page ||= 1)
page += 1
end until page_items.empty?
items.flatten
Here's how I'd have written it. You'll see it's actually more lines, but it's easier to read and more Rubyish.
def get_all_items
items = []
page = 1
page_items = get_page_items page
until page_items.empty? # Loop runs until no more items are received
items += page_items
page += 1
page_items = get_page_items page
end
items
end
You could also implement get_page_items as an Enumerator which would eliminate the awkward page += 1 pattern but that might be overkill.
I don't know that this is any better, but it does have a couple of Ruby-isms in it:
def get_all_items
items = []; n = 0; page = 1
while items.push(*get_page_items(page)).length > n
page += 1
n = items.length
end
end
I would use this solution, which is a good compromise between readability and length:
def get_all_items
[].tap do |items|
page = 0
until (page_items = get_page_items(page)).empty?
items << page_items
page += 1
end
end
end
The short version, just for fun ;-)
i=[]; p=0; loop { i+=get_page_items(p+=1).tap { |r| return i if r.empty? } }
I wanted to write a functional solution which would closely resemble the task you want to achieve.
I'd say that your solution comes down to this:
For all page numbers from 1 on, you get the corresponding list of
items; Take lists while they are not empty, and join them into a
single array.
Sounds ok?
Now let's try to translate this, almost literally, to Ruby:
(1..Float::INFINITY). # For all page numbers from 1 on
map{|page| get_page_items page}. # get the corresponding list of items
take_while{|items| !items.empty?}. # Take lists while they are not empty
inject(&:+) # and join them into a single array.
Unfortunately, the above code won't work right away, as Ruby's map is not lazy, i.e. it would try to evaluate on all members of the infinite range first, before our take_while had the chance to peek at the values.
However, implementing a lazy map is not that hard at all, and it could be useful for other stuff. Here's one straightforward implementation, along with nice examples in the blog post.
module Enumerable
def lazy_map
Enumerator.new do |yielder|
self.each do |value|
yielder.yield(yield value)
end
end
end
end
Along with a mockup of your actual HTTP call, which returns arrays of random length between 0 and 4:
# This simulates actual HTTP call, sometimes returning an empty array
def get_page_items page
(1..rand(5)).to_a
end
Now we have all the needed parts to solve our problem easily:
(1..Float::INFINITY). # For all page numbers from 1 on
lazy_map{|page| get_page_items page}. # get the corresponding list of items
take_while{|items| !items.empty?}. # Take lists while they are not empty
inject(&:+) # and join them into a single array.
#=> [1, 1, 2, 3, 1]
It's a small (and almost entirely cosmetic) tweak, but one option would be to replace while page_items != [] with until page_items.empty?. It's a little more "Ruby-ish," in my opinion, which is what you're asking about.
def get_all_items
items = []; page = 0
items << page_items while (page_items = get_page_items(page += 1))
items
end