Implementing &:method with custom methods - ruby

I've already read this question but I'm having trouble implementing the concepts there.
I'm doing an exercise from exercism.io that has provided tests. The aim of the exercise is to implement an accumulate method that returns the squares of the numbers passed to it. We need to do this without using map/inject.
That was no problem but one of the tests is as follows:
def test_accumulate_upcases
result = %w(hello world).accumulate(&:upcase)
assert_equal %w(HELLO WORLD), result
end
I have the following class
class Array
def accumulate
squares = []
self.each { |x| squares << x**2 unless x.is_a? String }
squares
end
def upcase
upcase = []
self.each { |word| word.upcase }
upcase
end
end
But I don't fully understand the concept being tested. How to I get accumulate to call methods that are passed to it as arguments?

It seems you are expected to extend Array class with new method accumulate which will accumulate the results of invoking a given proc on each element of the array.
One implementation can be like this:
class Array
def accumulate(&block)
self.collect { |i| block.yield(i) }
end
end
p result = %w(hello world).accumulate(&:upcase) # Prints ["HELLO", "WORLD"]
p result = %w(hello world).accumulate { |b| b.upcase } # Prints ["HELLO", "WORLD"]
Please note that %w(HELLO WORLD) is same as array ["HELLO", "WORLD"]
There is a very good explanation of what is the use of & operator in this article - please read the section on The Unary &

accumulate method is expecting to receive a name of the method as Symbol.
def accumulate meth = nil
result = []
case
when meth.nil?
self.each { |e| result << e.public_send meth }
when block_given?
self.each { |e| result << yield e }
else
fail 'This method expects either block or method name'
end
end

Related

Is there any difference in using `yield self` in a method with parameter `&block` and `yield self` in a method without a parameter `&block`?

I understand that
def a(&block)
block.call(self)
end
and
def a()
yield self
end
lead to the same result, if I assume that there is such a block a {}. My question is - since I stumbled over some code like that, whether it makes any difference or if there is any advantage of having (if I do not use the variable/reference block otherwise):
def a(&block)
yield self
end
This is a concrete case where i do not understand the use of &block:
def rule(code, name, &block)
#rules = [] if #rules.nil?
#rules << Rule.new(code, name)
yield self
end
The only advantage I can think of is for introspection:
def foo; end
def bar(&blk); end
method(:foo).parameters #=> []
method(:bar).parameters #=> [[:block, :blk]]
IDEs and documentation generators could take advantage of this. However, it does not affect Ruby's argument passing. When calling a method, you can pass or omit a block, regardless of whether it is declared or invoked.
The main difference between
def pass_block
yield
end
pass_block { 'hi' } #=> 'hi'
and
def pass_proc(&blk)
blk.call
end
pass_proc { 'hi' } #=> 'hi'
is that, blk, an instance of Proc, is an object and therefore can be passed to other methods. By contrast, blocks are not objects and therefore cannot be passed around.
def pass_proc(&blk)
puts "blk.is_a?(Proc)=#{blk.is_a?(Proc)}"
receive_proc(blk)
end
def receive_proc(proc)
proc.call
end
pass_proc { 'ho' }
blk.is_a?(Proc)=true
#=> "ho"

what's main use of yield method in ruby? [duplicate]

def any?
if block_given?
method_missing(:any?) { |*block_args| yield(*block_args) }
else
!empty?
end
end
In this code from ActiveRecord, what is the purpose of a yield statement that exists within a block?
Basically if the current method has been given a code-block (by the caller, when it was invoked), the yield executes the code block passing in the specified parameters.
[1,2,3,4,5].each { |x| puts x }
Now { |x| puts x} is the code-block (x is a parameter) passed to the each method of Array. The Array#each implementation would iterate over itself and call your block multiple times with x = each_element
pseudocode
def each
#iterate over yourself
yield( current_element )
end
Hence it results
1
2
3
4
5
The *block_args is a Ruby way to accept an unknown number of parameters as an array. The caller can pass in blocks with different number of arguments.
Finally let's see what yield within a block does.
class MyClass
def print_table(array, &block)
array.each{|x| yield x}
end
end
MyClass.new.print_table( [1,2,3,4,5] ) { |array_element|
10.times{|i| puts "#{i} x #{array_element} = #{i*array_element}" }
puts "-----END OF TABLE----"
}
Here Array#each yields each element to the block given to MyClass#print_table...
It does not mean anything special. It's just a yield like any other yield.
def test_method
["a", "b", "c"].map {|i| yield(i) }
end
p test_method {|i| i.upcase }
# => ["A", "B", "C"]
In the code snippet from active record, the purpose is to yield every time the block of any? is called.
This helped me understand: yield is a way to insert blocks into a method you already wrote, which means "execute something here". For instance,
Without parameter
def original_method
puts "a"
puts "b"
end
What if you want to execute some unknown block between those two p?
def updated_method
puts "a"
yield
puts "b"
end
# these two are the same:
updated_method { puts "execute something here" }
updated_method do puts "execute something here" end
The result would be
a
execute something here
b
You can have as many yields in a method as you like.
With parameter
def updated_method
puts "a"
yield
puts "b"
end
What if you want to execute some unknown block on the string "Execute something on me!"?
def updated_method_with_argument
puts "a"
yield("Execute something on me!")
puts "b"
end
updated_method_with_argument do |argument|
puts "This argument gets put out: " << argument
end
The result would be
a
This argument gets put out: Execute something on me!
b

Calls block once for each element of an array, passing that element as a parameter

I have some difficulties for using Ruby block, passing in a method.
As in the following case, I would like to display each element of #array, from Box instance (using .each method):
class Box
def initialize
#array = [:foo, :bar]
end
def each(&block)
# well, hm..
end
end
a = Box.new
a.each { |element| puts element }
You really just need to delegate to the each method on #array and pass it the block. Additionally, you can include the Enumerable mix-in to gain access to the methods it provides (e.g. map, inject, etc...):
class Box
include Enumerable
def initialize
#array = [:foo, :bar]
end
def each(&block)
#array.each(&block)
end
end
More information on the Enumerable module is available in the documentation.
For this simple example, you actually aren't required to pass the block explicitly:
def each
#array.each{|e| yield e}
end
Passing the block (which is a Proc object) explicitly allows you to test it for things, like the number of arguments that it expects:
class Box
...
def each(&block)
#array.each do |e|
case block.arity
when 0
yield
when 1
yield e
when 2
yield e, :baz
else
yield
end
end
end
end
a = Box.new
a.each { puts "nothing" } # displays "nothing, nothing"
a.each { |e| puts e } # displays "foo, bar"
a.each { |e1, e2| puts "#{e1} #{e2}" } # displays "foo baz, bar baz"

Call a block method on an iterator: each.magic.collect { ... }

I have a class with a custom each-method:
class CurseArray < Array
def each_safe
each do |element|
unless element =~ /bad/
yield element
end
end
end
end
And want to call different block methods, like "collect" or "inject" on those iterated elements. For example:
curse_array.each_safe.magic.collect {|element| "#{element} is a nice sentence."}
I know there is a specific function (which I called "magic" here) to do this, but I've forgotten. Please help! :-)
If a method yields you will need to pass it a block. There is no way define a block that automatically passes itself.
Closest I can get to your spec is this:
def magic(meth)
to_enum(meth)
end
def test
yield 1
yield 2
end
magic(:test).to_a
# returns: [1,2]
The cleanest way of implementing your request is probably:
class MyArray < Array
def each_safe
return to_enum :each_safe unless block_given?
each{|item| yield item unless item =~ /bad/}
end
end
a = MyArray.new
a << "good"; a << "bad"
a.each_safe.to_a
# returns ["good"]
The way you wrote your each_safe method, the easiest would be
curse_array.each_safe { |element| do_something_with(element) }
Edit: Oh, your each_safe method isn't correct, either. It has to be "each do", not "each.do"
Edit 2: If you really want to be able to do things like "each_safe.map", while at the same time also being able to do "each_safe { ... }" you could write your method like this:
require 'enumerator'
class CurseArray < Array
BLACKLIST = /bad/
def each_safe
arr = []
each do |element|
unless element =~ BLACKLIST
if block_given?
yield element
else
arr << element
end
end
end
unless block_given?
return Enumerator.new(arr)
end
end
end
The selected solution uses the common idiom to_enum :method_name unless block_given? which it's ok, but there are alternatives:
Leave your "unfriendly" yielder method untouched, use enum_for when calling it.
Use a lazy Enumerator.
Use lazy arrays (needs Ruby 2.0 or gem enumerable-lazy).
Here's a demo code:
class CurseArray < Array
def each_safe
each do |element|
unless element =~ /bad/
yield element
end
end
end
def each_safe2
Enumerator.new do |enum|
each do |element|
unless element =~ /bad/
enum.yield element
end
end
end
end
def each_safe3
lazy.map do |element|
unless element =~ /bad/
element
end
end.reject(&:nil?)
end
end
xs = CurseArray.new(["good1", "bad1", "good2"])
xs.enum_for(:each_safe).select { |x| x.length > 1 }
xs.each_safe2.select { |x| x.length > 1 }
xs.each_safe3.select { |x| x.length > 1 }.to_a

What does it mean to yield within a block?

def any?
if block_given?
method_missing(:any?) { |*block_args| yield(*block_args) }
else
!empty?
end
end
In this code from ActiveRecord, what is the purpose of a yield statement that exists within a block?
Basically if the current method has been given a code-block (by the caller, when it was invoked), the yield executes the code block passing in the specified parameters.
[1,2,3,4,5].each { |x| puts x }
Now { |x| puts x} is the code-block (x is a parameter) passed to the each method of Array. The Array#each implementation would iterate over itself and call your block multiple times with x = each_element
pseudocode
def each
#iterate over yourself
yield( current_element )
end
Hence it results
1
2
3
4
5
The *block_args is a Ruby way to accept an unknown number of parameters as an array. The caller can pass in blocks with different number of arguments.
Finally let's see what yield within a block does.
class MyClass
def print_table(array, &block)
array.each{|x| yield x}
end
end
MyClass.new.print_table( [1,2,3,4,5] ) { |array_element|
10.times{|i| puts "#{i} x #{array_element} = #{i*array_element}" }
puts "-----END OF TABLE----"
}
Here Array#each yields each element to the block given to MyClass#print_table...
It does not mean anything special. It's just a yield like any other yield.
def test_method
["a", "b", "c"].map {|i| yield(i) }
end
p test_method {|i| i.upcase }
# => ["A", "B", "C"]
In the code snippet from active record, the purpose is to yield every time the block of any? is called.
This helped me understand: yield is a way to insert blocks into a method you already wrote, which means "execute something here". For instance,
Without parameter
def original_method
puts "a"
puts "b"
end
What if you want to execute some unknown block between those two p?
def updated_method
puts "a"
yield
puts "b"
end
# these two are the same:
updated_method { puts "execute something here" }
updated_method do puts "execute something here" end
The result would be
a
execute something here
b
You can have as many yields in a method as you like.
With parameter
def updated_method
puts "a"
yield
puts "b"
end
What if you want to execute some unknown block on the string "Execute something on me!"?
def updated_method_with_argument
puts "a"
yield("Execute something on me!")
puts "b"
end
updated_method_with_argument do |argument|
puts "This argument gets put out: " << argument
end
The result would be
a
This argument gets put out: Execute something on me!
b

Resources