I have an array a = [1, 2, 3, 4, 5]
If I have an element in the array, I can find the next element in the array via the
a[0].next
Is there a method that I can use to find the previous element
a[1].previous or a[1].before such that the result is
#=> 1
I Googled around a bit and it seems as if most other similar questions involve a loop which I don't want to do. I also checked out the Ruby docs of Enumerator & Enumerable and couldn't seem to find one.
>> a = [1, 2, 3, 4, 5] #=> [1, 2, 3, 4, 5]
>> a[0].next #=> 2
>> a[2].previous
NoMethodError: undefined method `previous' for 3:Fixnum
from (irb):14
from /Users/Lois/.rvm/rubies/ruby-2.3.1/bin/irb:11:in `<main>'
>> a[2].before
NoMethodError: undefined method `before' for 3:Fixnum
from (irb):15
from /Users/Lois/.rvm/rubies/ruby-2.3.1/bin/irb:11:in `<main>'
Thanks in advance!
If you have an array:
a = [ 1, 2, 3, 4 ]
Then calling a[1] will return the object 2, and there's nothing special about that value, it's without context once you've retrieved it. As such, calling next on it will always yield 3 because that's what Integer#next does.
In order to navigate through that array you need an iterator of some sort, like Enumerator:
e = a.each
Now you can do what you want:
e.next
# => 1
e.next
# => 2
Note that this interface isn't as flexible as you're expecting. You can call next to advance, or rewind to go back to the beginning, but there's no previous. Ruby doesn't have what other languages term as bi-directional iterators, but you might be able to extend this class to add the functions you need if you're feeling brave.
I always end up whipping up something like this.
This approach, of course, isn't limited to Arrays - I didn't want to make it an EnumerableScanner, however, as some enumerations maintain complex state (think of a fibonacci sequence).
class ArrayScanner
attr_reader :index, :array
def initialize(array)
#array = array
#index = 0
end
def next
raise StopIteration unless next?
#array[#index += 1]
end
def peek
raise StopIteration unless next?
#array[#index + 1]
end
def next?
#index + 1 != #array.size
end
def prev
raise StopIteration unless prev?
#array[#index -= 1]
end
def peek_prev
raise StopIteration unless prev?
#array[#index - 1]
end
def prev?
#index - 1 >= 0
end
def eof?
!next?
end
def bof?
!prev?
end
def current
#array[#index]
end
def current=(new_value)
#array[#index] = new_value
end
def size
#array.size
end
def pos
#index
end
def rewind
#index = 0
end
end
a = ArrayScanner.new [1, 2, 3, 4, 5]
a.current #=> 1
a.next #=> 2
a.current #=> 2
a.prev #=> 1
a.prev? #=> false
a.bof? #=> true
4.times { a.next }
a.eof? #=> true
a.current #=> 5
The crystal language has a similar approach, with it's Iterator module.
a = [1, 2, 3, 4, 5]
i = 0
a[i] #=> 1
a[i.next] #=> 2
a[i.pred] #=> 5
i = 1
s = 1
a[i] #=> 2
a[i + s] #=> 3
a[i - s] #=> 1
Related
I'm working on a lab Using a generalized map method to pass an element and block through returning multiple outcomes.
Really struggled on this one. Found some responses but they don't really make sense to me.
Here is the code:
def map(s)
new = []
i = 0
while i < s.length
new.push(yield(s[i]))
i += 1
end
new
end
Here's is the test:
it "returns an array with all values made negative" do
expect(map([1, 2, 3, -9]){|n| n * -1}).to eq([-1, -2, -3, 9])
end
it "returns an array with the original values" do
dune = ["paul", "gurney", "vladimir", "jessica", "chani"]
expect(map(dune){|n| n}).to eq(dune)
end
it "returns an array with the original values multiplied by 2" do
expect(map([1, 2, 3, -9]){|n| n * 2}).to eq([2, 4, 6, -18])
end
it "returns an array with the original values squared" do
expect(map([1, 2, 3, -9]){|n| n * n}).to eq([1, 4, 9, 81])
end
end
I don't get how the above code can give you these 4 different results.
Could someone help me understand it ?
Thank you for your help!
How your method map works
To see how your method operates let's modify your code to add some intermediate variables and some puts statements to show the values of those variables.
def map(s)
new = []
i = 0
n = s.length
puts "s has length #{n}"
while i < n
puts "i = #{i}"
e = s[i]
puts " Yield #{e} to the block"
rv = yield(e)
puts " The block's return value is #{rv}. Push #{rv} onto new"
new.push(rv)
puts " new now equals #{new}"
i += 1
end
puts "We now return the value of new"
new
end
Now let's execute the method with one of the blocks of interest.
s = [1, 2, 3, -9]
map(s) { |n| n * 2 }
#=> [2, 4, 6, -18] (return value of method)
The following is displayed.
s has length 4
i = 0
Yield 1 to the block
The block's return value is 2. Push 2 onto new
new now equals [2]
i = 1
Yield 2 to the block
The block's return value is 4. Push 4 onto new
new now equals [2, 4]
i = 2
Yield 3 to the block
The block's return value is 6. Push 6 onto new
new now equals [2, 4, 6]
i = 3
Yield -9 to the block
The block's return value is -18. Push -18 onto new
new now equals [2, 4, 6, -18]
We now return the value of new
It may by of interest to execute this modified method with different values of s and different blocks.
A replacement for Array#map?
Is this a replacement for Array#map (or Enumerable#map, but for now let's just consider Array#map)? As you defined it at the top level your map is an instance method of the class Object:
Object.instance_methods.include?(:map) #=> true
It must be invoked map([1,2,3]) { |n| ... } whereas Array#map is invoked [1,2,3].map { |n| ... }. Therefore, for your method map to be a replacement for Array#map you need to define it as follows.
class Array
def map
new = []
i = 0
while i < length
new.push(yield(self[i]))
i += 1
end
new
end
end
[1, 2, 3, -9].map { |n| n * 2 }
#=> [2, 4, 6, -18]
Simplify
We can simplify this method as follows.
class Array
def map
new = []
each { |e| new << yield(e) }
new
end
end
[1, 2, 3, -9].map { |n| n * 2 }
#=> [2, 4, 6, -18]
or, better:
class Array
def map
each_with_object([]) { |e,new| new << yield(e) }
end
end
See Enumerable#each_with_object.
Note that while i < length is equivalent to while i < self.length, because self., if omitted, is implicit, and therefore redundant. Similarly, each { |e| new << yield(e) } is equivalent to self.each { |e| new << yield(e) } and each_with_object([]) { ... } is equivalent to self.each_with_object([]) { ... }.
Are we finished?
If we examine the doc Array#map carefully we see that there are two forms of the method. The first is when map takes a block. Our method Array#map mimics that behaviour and that is the only behaviour needed to satisfy the given rspec tests.
There is a second form, however, where map is not given a block, in which case it returns an enumerator. That allows us to chain the method to another. For example (with Ruby's Array#map),
['cat', 'dog', 'pig'].map.with_index do |animal, i|
i.even? ? animal.upcase : animal
end
#=> ["CAT", "dog", "PIG"]
We could modify our Array#map to incorporate this second behaviour as follows.
class Array
def map
if block_given?
each_with_object([]) { |e,new| new << yield(e) }
else
to_enum(:map)
end
end
end
[1, 2, 3, -9].map { |n| n * 2 }
#=> [2, 4, 6, -18]
['cat', 'dog', 'pig'].map.with_index do |animal, i|
i.even? ? animal.upcase : animal
end
#=> ["CAT", "dog", "PIG"]
See Kernel#block_given? and Object#to_enum.
Notes
You might use, say, arr, rather than s as the variable holding the array, as s often denotes a string, just as h typically denotes a hash. One generally avoids names for variables and custom methods that are the names of core Ruby methods. That is also an objection to your use of new as a variable name, as there are many core methods named new.
Let's say I have two enumerators, enum1 and enum2 that must be lazily iterated through (because they have side effects). How do I construct a third enumerator enum3 where enum3.each{|x| x} would lazily return the equivalent of enum1 + enum2?
In my real world use case, I'm streaming in two files, and need to stream out the concatenation.
This seems to work just how I want;
enums.lazy.flat_map{|enum| enum.lazy }
Here's the demonstration. Define these yielding methods with side-effects;
def test_enum
return enum_for __method__ unless block_given?
puts 'hi'
yield 1
puts 'hi again'
yield 2
end
def test_enum2
return enum_for __method__ unless block_given?
puts :a
yield :a
puts :b
yield :b
end
concated_enum = [test_enum, test_enum2].lazy.flat_map{|en| en.lazy }
Then call next on the result, showing that the side effects happen lazily;
[5] pry(main)> concated_enum.next
hi
=> 1
[6] pry(main)> concated_enum.next
hi again
=> 2
Here's some code I wrote for fun awhile back with lazy enumeration thrown in:
def cat(*args)
args = args.to_enum
Enumerator.new do |yielder|
enum = args.next.lazy
loop do
begin
yielder << enum.next
rescue StopIteration
enum = args.next.lazy
end
end
end
end
You would use it like this:
enum1 = [1,2,3]
enum2 = [4,5,6]
enum3 = cat(enum1, enum2)
enum3.each do |n|
puts n
end
# => 1
# 2
# 3
# 4
# 5
# 6
...or just:
cat([1,2,3],[4,5,6]).each {|n| puts n }
Since Ruby 2.6 you can use Enumerable#chain/Enumerator::Chain:
a = [1, 2, 3].lazy
b = [4, 5, 6].lazy
a.chain(b).to_a
# => [1, 2, 3, 4, 5, 6]
Enumerator::Chain.new(a, b).to_a
# => [1, 2, 3, 4, 5, 6]
If I do this:
class PseudoRelationship
def my_method(args)
args
end
end
a = PseudoRelationship.new
I got as output
x
#<PseudoRelationship:0x109c2ebb0>
I would like it to behave like a Enumerator or Array, so I get for example this output
x = PseudoRelationship.new [1,2,3]
x
[1,2,3]
Pd. This is not for rails.
What I'm trying to do is behave like an array.
Its something that rails 2.3 seems to use, for example, you can do
my_model.relationship # returns an array
my_model.relationship.find #is a method
I'm trying to replicate that behaviour.
jholtrop was close, you want to overwrite the inspect method
2.0.0p645>
class PseudoRelationship
def initialize(args)
#args = args
end
def inspect
#args.inspect
end
end
2.0.0p645> PseudoRelationship.new [2, 3, 5]
[2, 3, 5]
--Edit based on OP's reasons for wanting this behavior--
While the above class displays what we want to see in the console, it doesn't actually assume any management of args in a way that treats args as an Enumerable. The OP's inspiration is drawn from a Rails construct, the ActiveRecord::Relation*. To imulate that style of behavior, you have to include Enumerable.
class PseudoRelationship
include Enumerable
def initialize(args)
#args = args
end
def each(&block)
#args.each(&block)
end
def inspect
#args.inspect
end
# Add extra functions to operate on #args
# This is obviously a silly example
def foo?
#args.include? :foo
end
def [](key)
#args[key]
end
def last
#args[-1]
end
end
2.0.0p645> PseudoRelationship.new [2, 3, 5]
[2, 3, 5]
2.0.0p645> x = PseudoRelationship.new [2, 3, 5]
[2, 3, 5]
2.0.0p645> x.each
#<Enumerator: [2, 3, 5]:each>
2.0.0p645> x.each_with_index
#<Enumerator: [2, 3, 5]:each_with_index>
2.0.0p645> x.each_with_index { |e, i| puts "#{i} => #{e}" }
0 => 2
1 => 3
2 => 5
[2, 3, 5]
2.0.0p645> x.foo?
false
2.0.0p645> x.first
2
2.0.0p645> x.last
5
2.0.0p645> x[1]
3
2.0.0p645> x[5]
nil
2.0.0p645> x
[2, 3, 5]
* This construct wasn't explictly stated, but I'm assuming based on the context
It sounds like you are asking to alter the "representation" of the object, not the "return value." To give the object a new String representation, you can define the #to_s method on the class. For example:
class PseudoRelationship
def initialize(v)
#v = v
end
def to_s
#v.to_s
end
end
Then in irb, for example:
1.9.3-p551 :021 > x = PseudoRelationship.new [:a, 42, false]
=> [:a, 42, false]
If you want the variable x to equal the arguments passed when instantiating the object, you need to add an initialize method to your class. Something like this should work:
class PseudoRelationship
def initialize(args)
args
end
def my_method(args)
args
end
end
x = PseudoRelationship.new [1, 2, 3]
x == [1, 2, 3] #=> true
EDIT:
Yes, the above won't work, really. If you want it to behave like an array, you could inherit from Array, but that can lead to strange issues and is generally not recommended. However, the below would work:
class PseudoRelationship < Array
end
x = PseudoRelationship.new [1, 2, 3]
x.class #=> PseudoRelationship < Array
x == [1, 2, 3] #=> true
I'm writing a program that pushes Fibonacci numbers into an array, using Ruby. The code works, but I can't wrap my head around why it works.
This part I understand, it's the Fibonacci equation:
fib_array = []
def fib (n)
return n if n <= 1
fib(n - 1) + fib(n - 2)
end
This is what I don't understand:
10.times do |x|
fib_array << fib(x)
end
print fib_array
I wrote this grasping at straws, and it works. I don't understand why. I didn't feed it a number to start at, does Ruby take that to mean 0? Also, how did it know to compound the numbers instead of printing [0, 0, 0...]? I apologize if this is a dunderheaded question, but I'm at loss.
It looks like the bottom piece of code simply calls the fib function on x=0, x=1 ... x=9 and stores it's return value at the end of the array. When times is invoked with an iteration variable (x), it begins at 0 and increments on each iteration through the loop. You never fed it a value, however it manages to successfully solve the problem with the iteration variable x being passed in as the parameter to fib.
The second part of your code says:
"From the instance 10 of the class Integer, call the method times with the given block" (The method "recive" a block implicitly).
What is a block? A small piece of code between {braces} or a do-end (like you did).
The method times is called, "iterator". And it will yield 0,1,2,..,9 (in your case). An iterator and the yield statement are always together. Think that yield is like return with memory, when you look for more information.
So, your code could be re-writing like:
10.times { |x| fib_array << fib(x) }
And it will call, the block you pass, on every yield that the method times
does. Calling the method << (append) to the result of fib(x) on your array.
We have:
def fib (n)
return n if n <= 1
fib(n - 1) + fib(n - 2)
end
which you understand. First let's see what are the first 5 Fibonacci numbers::
fib_array = []
5.times do |x|
fib_array << fib(x)
end
fib_array
#=> [0, 1, 1, 2, 3]
Now let's break this down and see what happening. First look at the docs for the method times. To find them, we need to know what class or module the method is from, because that's how the docs are organized. As 5.class #=> Fixnum, we might look at the docs for Fixnum. Hmmm. times is not there. Evidentally, it was inherited from another class. Let's check:
Fixnum.ancestors
#=> [Fixnum, Integer, Numeric, Comparable, Object, Kernel, BasicObject]
Integer includes Fixnum and BigNum, Numeric includes Integer and Float. (3.14).times doesn't make sense, so it appears times is defined in Integer, and so it is: Integer#times. By defining it there, it is inherited by both Fixnum and Bignum.
Here's a direct way to determine where the method came from:
5.method(:times).owner #=> Integer
It's not important understand this now, but you'll find it handy as you gain experience with Ruby.
OK, the docs say that times return a value if given a block, or an enumerator if not. Let's forget about the block a moment and look at the enumerator that is returned:
enum = 5.times #=> #<Enumerator: 5:times>
The method Enumerator#each passes the elements of the enumerator enum to its block:
do |x|
fib_array << fib(x)
end
assigning them to the block variable x. To see the contents of the enumerator, convert it to an array:
enum.to_a #=> [0, 1, 2, 3, 4]
The result is:
fib_array = []
enum.each do |x|
fib_array << fib(x)
end
fib_array
#=> [0, 1, 1, 2, 3]
which of course is the same result that we obtained previously. Now let's see what's happening step-by-by, by using the method Enumerator#next to extract each element of the enumerator:
x = enum.next #=> 0
fib(x) #=> 0
fib_array << fib(x) #=> [0]
x = enum.next #=> 1
fib(x) #=> 1
fib_array << fib(x) #=> [0, 1]
x = enum.next #=> 2
fib(x) #=> 1
fib_array << fib(x) #=> [0, 1, 1]
x = enum.next #=> 3
fib(x) #=> 2
fib_array << fib(x) #=> [0, 1, 1, 2]
x = enum.next #=> 4
fib(x) #=> 3
fib_array << fib(x) #=> [0, 1, 1, 2, 3]
print fib_array # [0, 1, 1, 2, 3]
That's all there is to it.
I think infinite enumerator is very convenient for writing FP style scripts but I have yet to find a comfortable way to construct such structure in Ruby.
I know I can construct it explicitly:
a = Enumerator.new do |y|
i = 0
loop do
y << i += 1
end
end
a.next #=> 1
a.next #=> 2
a.next #=> 3
...
but that's annoyingly wordy for such a simple structure.
Another approach is sort of a "hack" of using Float::INFINITY:
b = (1..Float::INFINITY).each
b = (1..1.0/0.0).each
These two are probably the least clumsy solution I can give. Although I'd like to know if there are some other more elegant way of constructing infinite enumerators. (By the way, why doesn't Ruby just make inf or infinity as a literal for Float::INFINITY?)
Use #to_enum or #lazy to convert your Range to an Enumerable. For example:
(1..Float::INFINITY).to_enum
(1..Float::INFINITY).lazy
I would personally create my own Ruby class for this.
class NaturalNumbers
def self.each
i = 0
loop { yield i += 1 }
end
end
NaturalNumbers.each do |i|
puts i
end
Ruby 2.7 introduced Enumerator#produce for creating an infinite enumerator from any block, which results in a very elegant, very functional way of implementing the original problem:
irb(main):001:0> NaturalNumbers = Enumerator.produce(0) { |x| x + 1 }
=> #<Enumerator: #<Enumerator::Producer:0x00007fadbd82d990>:each>
irb(main):002:0> NaturalNumbers.first(10)
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
irb(main):003:0> _
... which - if you're a fan of numbered block parameters (another Ruby 2.7 feature) - can also be written as:
irb(main):006:0> NaturalNumbers = Enumerator.produce(0) { _1 + 1 }
=> #<Enumerator: #<Enumerator::Producer:0x00007fadbc8b08f0>:each>
irb(main):007:0> NaturalNumbers.first(10)
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
irb(main):008:0> _