According to Ruby Hash/Array documentation, the delete_if method returns an enumerator if no block is given. How is this useful? Can someone give an example to demonstrate this pattern?
There are some methods defined on Enumerator that give flexibility to iterators. One such method I often use is with_index.
p %w[a b c d e f].delete_if.with_index{|_, i| i.even?}
# => ["b", "d", "f"]
If this was to be done without Enumerator class, all kinds of methods have to be defined, including delete_if_with_index, and that is not a good thing.
The enumerator will just allow you to run the block later. For example, if you had a method that specifically handled the delete if for several different objects, you could pass it the enumerator.
In the example below, it will print 1, 3, 5
arr = [0,1,2,3,4,5]
enumerator = arr.delete_if
enumerator.each { |el| el.even? }
puts arr.join(', ')
Related
I have:
letters = %w(e d c b a)
In the following:
letters.group_by.each_with_index { |item, index| index % 3 }
#=> {0=>["e", "b"], 1=>["d", "a"], 2=>["c"]}
how can the enumerator returned by group_by know the block it will execute? Is the block received by each_with_index passed to the enumerator which it is based on?
In the following:
letters.each_with_index.group_by { |item, index| index % 3 }
#=> {0=>[["e", 0], ["b", 3]], 1=>[["d", 1], ["a", 4]], 2=>[["c", 2]]}
will the block be passed to the enumerator returned by each_with_index? If it will, how does each_with_index execute it?
In general:
How is a block retrieved by a method in an enumerator that doesn't directly receive the block?
Will a block be passed through an enumerator chain? Where will it be executed?
There's some tricky stuff going on here, so that's probably why you're a little hazy on how it works. Enumerators are one of the most important things in Ruby, they're the backbone of the Enumerable system which is where Ruby really shines, but they're often used in ways where they're transparent, living in the shadows, so you rarely have to pay direct attention to them.
Looking more closely, step through this bit by bit:
letters.group_by
# => #<Enumerator: ["e", "d", "c", "b", "a"]:group_by>
Now this is an Enumerator instance. The each_with_index you chain on the end is actually an Enumerator-specific method:
letters.group_by.method(:each_with_index)
# => #<Method: Enumerator#each_with_index>
This is in contrast to your second approach:
letters.method(:each_with_index)
# => #<Method: Array(Enumerable)#each_with_index>
That one is Array's method which, conveniently, you can chain into a method like group_by.
So the story here is that group_by in chain mode actually provides special methods that have the effect of back-propagating your block to the group_by level.
I need Ruby method to convert an array of strings into a Hash where each key is a string and each value is the 1-indexed index of the string in the original array.
hashify(%w(a b c))
# should return
{'a' => 1, 'b' => 2, 'c' => 3}
Even though I think I'm helping someone do their homework, I can't resist taking a golf swing, because Ruby is awesome:
%w(a b c).each.with_index(1).to_h
Also, defining "hashify" is VERY un-Ruby-like. I'd suggest alternatives, but it's probably a homework assignment anyways and you don't seem to want to learn it.
def hashify(array)
array.each.with_index(1).to_h
end
hashify(%w(a b c))
#=> { "a" => 1, "b" => 2, "c" => 3 }
There are (clearly) multiple ways you could achieve your goal in Ruby.
If you consider the expression %w(a b c).map.with_index(1).to_h, you can see that it is a matter of stringing together a few methods provided to us by the Enumerable class.
By first sending the :map message to the array, we receive back an Enumerator, which provides us the handy :with_index method. As you can see in the docs, with_index accepts an offset in an argument, so offsetting your indices by 1 is as simple as passing 1 as your argument to :with_index.
Finally, we call :to_h on the enumerator to receive the desired hash.
# fore!
def hashify(array)
array.map.with_index(1).to_h
end
> hashify %w(a b c)
=> {"a"=>1, "b"=>2, "c"=>3}
Try this, this will add a method to_hash_one_indexed to the Array class.
class Array
def to_hash_one_indexed
map.with_index(1).to_h
end
end
Then to call it:
%w(a b c).to_hash_one_indexed
#=> {"a"=>1, "b"=>2, "c"=>3}
I don't see the use of enumerators. Dave Thomas' book states this:
Enumerator allows you to capture the concept of an enumeration as an object. This allows you to store enumerations in variables, pass them as parameters, and so on.
But I can also assign arrays to variables, pass arrays as arguments and so on. In fact, I think using a regular iterator with a collection like array is more concise than using an enumerator:
str = "quick brown fox"
str.scan(/\w+/).each {|w| puts w }
quick
brown
fox
vs.
str = "quick brown fox"
enum = str.to_enum(:scan, /\w+/)
enum.each { |w| puts w }
quick
brown
fox
The enum version requires an extra step and produces the same result. When is it more practical to use an enum over a collection with iterator?
Using an Enumerator makes some things possible that Arrays don't allow.
An Enumerator can represent a sequence that is lazily evaluated, meaning the code to generate an element doesn't get run until the element is needed.
That means that an Enumerator can represent an infinite sequence. For example:
e = Enumerator.new { |y| s = 'a'; loop { y << s = s.succ } }
e.first(5) # => ["b", "c", "d", "e", "f"]
This example shows an infinite sequence, which implies it has to be lazily evaluated because your computer is finite. But even if you aren't working with infinite sequences, the lazy evaluation can still be very helpful for saving CPU time.
Also, an Enumerator can be used an external iterator, which provides all sorts of flexibility in how you use it. (See 7stud's answer for an example.)
Enumerators are also external iterators, which allows you to iterate over two arrays at once:
e1 = [1, 2, 3].each
e2 = ['a', 'b', 'c'].each
loop do
num, ch = e1.next, e2.next
puts "#{num} -> #{ch}"
end
--output:--
1 -> a
2 -> b
3 -> c
When an Enumerator runs out of items, it raises a StopIteration exception, and loop() automatcially catches that exception and terminates.
It seems that the arguments are copied when using the splat operator to pass arguments to a block by reference.
I have this:
def method
a = [1,2,3]
yield(*a)
p a
end
method {|x,y,z| z = 0}
#=> this puts and returns [1, 2, 3] (didn't modified the third argument)
How can I pass these arguments by reference? It seems to work if I pass the array directly, but the splat operator would be much more practical, intuitive and maintainable here.
In Ruby when you write x = value you are creating a new local variable x whether it existed previously or not (if it existed the name is simply rebound and the original value remains untouched). So you won't be able to change a variable in-place this way.
Integers are immutable. So if you send an integer there is no way you can change its value. Note that you can change mutable objects (strings, hashes, arrays, ...):
def method
a = [1, 2, "hello"]
yield(*a)
p a
end
method { |x,y,z| z[1] = 'u' }
# [1, 2, "hullo"]
Note: I've tried to answer your question, now my opinion: updating arguments in methods or blocks leads to buggy code (you have no referential transparency anymore). Return the new value and let the caller update the variable itself if so inclined.
The problem here is the = sign. It makes the local variable z be assigned to another object.
Take this example with strings:
def method
a = ['a', 'b', 'c']
yield(*a)
p a
end
method { |x,y,z| z.upcase! } # => ["a", "b", "C"]
This clearly shows that z is the same as the third object of the array.
Another point here is your example is numeric. Fixnums have fixed ids; so, you can't change the number while maintaining the same object id. To change Fixnums, you must use = to assign a new number to the variable, instead of self-changing methods like inc! (such methods can't exist on Fixnums).
Yes... Array contains links for objects. In your code when you use yield(*a) then in block you works with variables which point to objects which were in array. Now look for code sample:
daz#daz-pc:~/projects/experiments$ irb
irb(main):001:0> a = 1
=> 1
irb(main):002:0> a.object_id
=> 3
irb(main):003:0> a = 2
=> 2
irb(main):004:0> a.object_id
=> 5
So in block you don't change old object, you just create another object and set it to the variable. But the array contain link to the old object.
Look at the debugging stuff:
def m
a = [1, 2]
p a[0].object_id
yield(*a)
p a[0].object_id
end
m { |a, b| p a.object_id; a = 0; p a.object_id }
Output:
3
3
1
3
How can I pass these arguments by reference?
You can't pass arguments by reference in Ruby. Ruby is pass-by-value. Always. No exceptions, no ifs, no buts.
It seems to work if I pass the array directly
I highly doubt that. You simply cannot pass arguments by reference in Ruby. Period.
Recently I've came up with this method:
module Enumerable
def transform
yield self
end
end
The purpose of method is similar to tap method but with the ability to modify object.
For example with this method I can change order in an array in chain style:
array.do_something.transform{ |a| [a[3],a[0],a[1],a[2]] }.do_something_else
Instead of doing this:
a0,a1,a2,a3 = array.do_something
result = [a3, a0, a1, a2].do_something_else
There are also another conveniences when using this method but...
The method is very straightforward, so I guess somewhere should be the already built method with the same purpose.
Is there analogue for this ruby method?
You can do that with instance_eval:
Evaluates (…) the given block, within the context of the receiver
Example:
%w(a b c d).instance_eval{|a| [a[3], a[0], a[1], a[2]] }
# => ["d", "a", "b", "c"]
or using self:
%w(a b c d).instance_eval{ [self[3], self[0], self[1], self[2]] }
# => ["d", "a", "b", "c"]
I can't test this now but you should be able to do something like this:
array= [1,2,3]
array.tap{ |a| a.clear }
Tap runs the block then returns self so if you can modify self in the block, it will pass back the updated array. In my example clear modifies self in the block so the modified self is returned.
If you want this functionality, I would suggest adding a method like do_something_else! that modifies self then running it within your tap block.
So where is the question? It all depends on how far you want to go into functional programming realm. Just read Learn You a Haskell for Great Good! and you will never be the same. Once you open this Pandora box it's really hard to stop and after some experimenting I wonder if I'm still writing Ruby code. Compare (using your transform method defined for Object)
h = {}
{:a => :a_method, :b => :b_method}.each do |k, m|
h[k] = some_object.__send__(m)
end
h.some_other_method
and
some_object.transform(&THash[:a => :a_method, :b => :b_method]).some_other_method
where
THash =
lambda do |t|
h = {}
kms = t.map { |k, v| [k, v.to_proc] }
lambda do |x|
kms.each { |k, m| h[k] = m[x] }
end
end
So if you want to think of your objects in terms of transformations, it makes perfect sense and does make code more readable, but it's more than just transform method, you need to define generic transformations you use frequently.
Basically it's called point-free programming, though some call it pointless. Depends on your mindset.