Obtaining number of block parameters - ruby

I need to obtain the number of parameters a given block takes. For example:
foobar(1,2,3) { |a, b, c|
}
def foobar(x, y, z, &block)
# need to obtain number of arguments in block
# which would be 3 in this example
end
This is possible in the 1.9 trunk, but not in any official release. I was hoping if there's any way to do this without having to download a separate gem/extension module.

When you materialize a block with &, it becomes a Proc object, which has an arity method. Just be careful - it returns the one's complement if the proc takes a *splat arg.
def foobar(x, y, z, &block)
p block.arity
end
(Answer via "The Ruby Programming Language" book.)

Is this what you're looking for...
def foobar(x, y, z, &block)
# need to obtain number of arguments in block
# which would be 3 in this example
case block.arity
when 0
yield "i have nothing"
when 1
yield "I got ONE block arg"
when 2
yield "I got TWO block args"
when 3
yield "I got THREE block args"
end
end
foobar(1,2,3) { |a, b, c|
puts a
}
Outputs:
D:\ruby\bin>ruby -v
ruby 1.8.6 (2007-09-24 patchlevel 111) [i386-mswin32]
D:\ruby\bin>ruby c:\Temp.rb
I got THREE block args
See also - A Ruby HOWTO: Writing A Method That Uses Code Blocks from codahale.com

Related

Ruby Yield and For Loop

I'm working my way through a simple tutorial of each vs for loops in Ruby. This is supposed to be one of the simpler examples but for some reason, I don't understand the interaction between the yield statements and the for loop.
class MyEachThing
def each
yield 1
yield 42
yield 2
yield 42
yield 3
end
end
for i in MyEachThing.new
p i
end
# >> 1
# >> 42
# >> 2
# >> 42
# >> 3
Yield in this next example that I made up makes sense to me:
def calling
p yield(45)
end
calling {|i| i*2}
I just don't get how the first example works. Thank you for the help.
for i in MyEachThing.new
p i
end
is similar to this:
MyEachThing.new.each do |i|
p i
end
which means, you are calling each method on MyEachThing instance and passing i to the block.
And, yield is equivalent to: block.call means, you're calling the block with the passed argument (in this case i).
yield i is equivalent to: block.call(i) and your block is just printing the value of i.

What do c == self and yield do?

Can you help me understand what this class does and how we can make use of it?
class Integer
def myt
c=0
until c == self
yield(c)
c+=1
end
self
end
end
Thank you.
x = Integer.new
x.myt
I tried to test it but it doesn't work. Error is: "no block given (yield)"
Also, in my book it says to test like this:
5.myt (|| puts "I'm on iteration #{i}! "} but it also gives an error - not sure why or what this line of code means.
allonhadaya and PNY did a good job explaining the purpose (enumeration) of the myt method.
Regarding your two questions mentioned in the title:
1.) What does 'c == self' do?
The '==' operator checks whether the integer c and Integer object you instantiate, are equal in value. If they are, the expression evaluates to true.
2.) What does 'yield' do?
The 'yield' statement passes control from the current method to a block which has been provided to the method. Blocks are ruby's implementation of a closure which, simple put, means that a method can be "extended" by calling the method with a block of additional code as long as the method supports a block (ie. incorporates yield statements)
The method seems to be a times implementation.
Basically 5.times { |i| puts i } and 5.myt { |i| puts i } will do exactly the same thing.
First, it sets a counter to 0, c = 0. Then you have a conditional where it checks if c is equal with self which will always be the integer attached to the method myt. It, then yields the counter and return self when is done.
Looks like it enumerates the values between zero inclusively and self exclusively.
allon#ahadaya:~$ irb
irb(main):001:0> class Integer
irb(main):002:1> def myt
irb(main):003:2> c=0
irb(main):004:2> until c == self
irb(main):005:3> yield(c)
irb(main):006:3> c+=1
irb(main):007:3> end
irb(main):008:2> self
irb(main):009:2> end
irb(main):010:1> end
=> nil
irb(main):011:0> 5.myt { |i| puts i }
0
1
2
3
4
=> 5
irb(main):012:0>
Using the example your book gave --
5.myt {|i| puts "I'm on iteration #{i}! "}
#You were missing an object in the pipes and a curly bracket before the pipes (not parentheses)
Allows you to see the internal workings of your myt method. Initializing variable c with a value of 0 the method executes an until look until the condition "c == self" is satisfied. Self references the object, here 5, which the method is acting on.
Therefore ...
def myt
until c == 5 #Until this is true
yield(c) #Do this .. here yield will do whatever the block specified
c+=1 #Increment on each iteration the value of variable c by 1
end #closing the until loop
self #return self
end
The yield within the method passes control from your method to the parameter, a block, back to the method.
Yield therefore allows you to build methods which can have similar patterns but with block you customize it to do your particular need.
If instead of putting each number maybe all you want to do is put the odd integers between 0 and the integer you call the method on --
5.myt {|i| puts i if i.odd?} # returns I am odd: 1 and I am odd: 3
I would suggest that you write your own blocks here to see how yield works and how you can keep the same method but pass in different blocks and create different method outputs!

DRYing argument checks

There's a method that's usually called with named arguments and it looks like this
def foo(x = nil, y = nil)
fail ArgumentError, "x must be present" unless x
fail ArgumentError, "y must be present" unless y
# do stuff with x and y
end
I want to rewrite as something like
def foo(x = nil, y = nil)
required_arguments :x, :y
# do stuff with x and y
end
or
class Foo
required_arguments :bar, :x, :y
def bar(x = nil, y = nil)
end
end
I've tried to implement second approach with alias_method_chain but the problem is that __method__ is evaluated in the context of my utility module so I can't access the parameters of the method I need to check. Any ideas?
If you use ruby 2.0 you can use keyword arguments:
def foo(x: (fail ArgumentError), y: (fail ArgumentError))
# do stuff with x and y
end
And in ruby 2.1 you have proper required arguments:
def foo(x:, y:)
# do stuff with x and y
end
This way you do actually have named parameters (you called them named parameters in your question, but that's a bit confusing imho), so you have to call the method like this:
foo(x: 1, y: 2)
The value of such run-time assertions is limited, but they do have their use. I have incorporated them in YSupport. Type gem install y_support in your command line, and use it as follows
require 'y_support/typing'
def foo x=nil, y=nil
x.aT; y.aT
# do stuff with x and y
end
Mnemonic: In aT, a means "assert" and T means TypeError – raise TypeError if the assertion fails. If #aT method is called without parameters, it simply enforces that the receiver must be truey. If a block is supplied, any assertion can be written. For example, the following call enforces that the receiver be divisible by 3:
6.aT { |n| n % 3 == 0 } #=> 6
7.aT { |n| n % 3 == 0 } #=> TypeError: 7:fixnum fails its check!
In case of checking the method arguments, ArgumentError is appropriate when there is wrong number of arguments and similar problems. When the arguments are of wrong type, I prefer to raise TypeError. Error message of #aT method can be customized with two string arguments. The first one describes what the receiver, the second one describes the block assertion. For example:
7.aT "number of apples", "be divisible by three" do |n| n % 3 == 0 end
#=> TypeError: Number of apples fails to be divisible by three!
The #aT method, if it passes, returns back its receiver, so the assertions can be chained:
81.aT( "no. of apples", "divisible by 3 ) { |n|
n % 3 == 0
}.aT( "no. of apples", "be a square" ) { |n|
root = n ** 0.5; root == root.floor
} ** 0.5 #=> 9.0
Other, more specialized runtime assertions are available in YSupport, such as:
[ 1, 2, 3 ].aT_kind_of Enumerable #=> [ 1, 2, 3 ]
:foobar.aT_respond_to :each
#=> TypeError: Foobar:symbol does not respond to method 'each'!
:foobar.aT_respond_to :each, "object returned from the black box"
#=> TypeError: Object returned from the black box does not respond to method 'each'!
7.aT_equal 8
#=> TypeError: 7:fixnum must be equal to the prescribed value (8:fixnum)!
You can look up more of those methods by yourself in YSupport, and if you miss something, you are welcome to contribute it.
As a post scriptum to this post, if you are accustomed to ActiveSupport's #present? method, the runtime assertion for it in YSupport is:
[].aT_present "supplied array"
#=> TypeError: Supplied array not present!

How to prevent the case when parameters do not match in yield?

For example, it causes problem when I use yield(param) but pass a block {|p1,p2| ...} which has two parameters. How should I prevent this if I do not know what parameters yield takes a head of time?
Use a splat in the block, or simply pass variables which may be there and test for their definition:
def foo
yield 1, 2, 3
end
foo do |*args|
if 3 == args.length
# ... then I know I'm dealing with 3 args
end
end
foo do |a, b, c, d|
if !d.nil?
# ... then I know i was passed `d`
end
end
See this excellent blog post on Ruby Blocks, Procs, and Lambdas:
Understanding Ruby Blocks, Procs and Lambdas
It explains that Blocks (such as the one being passed in foo() {|p1,p2| ...}) act like Procs, which do not check the number of arguments passed. Lambdas do, however. So if you require strict checking of the number of arguments, you could potentially switch from accepting a Block in your method to accepting a Lambda as a method argument.
You should use a block argument instead of yield, which provides more information than yield such as arity.
def foo *params, &block
...
block.call(*params) if params.length == block.arity
...
end
foo(3){|x| puts x*3} # => 9
foo(3, 4){|x| puts x*3} # => Does not do anything

In ruby, what does "&block" do? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What’s this &block in Ruby? And how does it get passes in a method here?
I dont Understand the &block part, what does it do?
here is an example:
def method_missing(method_name, *args, &block)
#messages << method_name
#object.send method_name, *args, &block
end
Blocks give you an opportunity to state a callback to pass on to a method.
The & is key here - like #pst mentioned, it "promotes" the block to a Proc and binds the Proc to the variable with the given name.
With &
def time(&block)
puts block
end
time
# => nil
time { foo }
# => #<Proc:0x00029bbc>
Without &
def time(block)
puts block
end
time { foo }
# => ArgumentError: wrong number of arguments (0 for 1)
# Because & isn't included, the method instead expected an arguement,
# but as a block isn't a arguement an error is returned.
Answering "And how would I pass it to another method?" comment by Brian:
Like this:
def compose init_value, n=2, &b
if n==0 then init_value else
b.call( compose init_value, n - 1, &b )
end
end
compose 2 do |n| n * n end
#=> 16
compose 2, 4 do |n| n * n end
#=> 65536
compose 2, 4 do |n| n * 0.5 end
#=> 0.125
This is a recursive method that recursively applies the same block to a number several times. Here, the block packaged into b argument gets called, but at the same time it is passed on recursively to compose method, while n argument is decremented by 1. In the same way, b could be passed to any method, like map, reduce, anything.
Whereas, should you not need to pass the block to another method, you could simply use yield:
def apply_block_to_1_2_3
return yield( 1 ), yield( 2 ), yield( 3 )
end
apply_block_to_1_2_3 { |n| n * n }
#=> [1, 4, 9]
May the force be with you.
It converts the block to a proc object that can be passed on to another method.
when you call a method with a block, there are 2 ways to use that block:
call yield inside method
convert it into a Proc object by prepending & to it
with second way you can pass it to another method.
so in your case it transforms the given block into a Proc and calling method_name with it.
think of it as you can pass a block just like any argument.

Resources