How can I access the arguments? - ruby

I want a method to be used within another method and returns the arguments without the need of mentioning the argument names, something like return_arguments in the following.
def foo a, b, *c
... # part a
p return_arguments
... # part b
end
foo(1, "blah blah", :a, :b)
... # return from part a
# => [1, "blah blah", :a, :b]
... # return from part b
Is this possible? I figured out that binding, local_variables, and eval may be used, but am not sure how to distinguish the arguments from other local variables defined in part a above. Is there a particular ordering rule for local_variables? If so, maybe I can use that together with arity to extract the arguments.

It's definitely inefficient, but here's a solution (although you probably should be using a hash like #tadman mentioned):
set_trace_func proc { |e,_,_,i,b,_|
if e == 'call'
p = method(i).parameters
#arguments = p.map {|p| eval(p[1].to_s,b)}
end
}
def foo a, b, *c
a = 50
b = 3
return #arguments
a = 11
b = 2
end
p foo(1, "blah blah", :a, :b)

Use local_variables first thing in the method, i.e. before setting any local variables, and save the result for later use.

Probably the easiest thing to do would be something like this:
def foo(*args)
a, b, *c = *args
# (...)
p args
# (...)
end
Or conversely:
def foo(a, b, *c)
args = [a, b, *c]
# (...)
p args
# (...)
end
I'm not sure why you are opposed to mentioning the argument names at least once. It's most straightforward and readable to either assign your "arguments" inside your method (letting to keep the full array) or to reconstruct the array from the parsed arguments.

Related

Order of optional parameters in initializer

Why am I getting the following error
a.rb:4: syntax error, unexpected '=', expecting ')'
...alize(a = "default val", b, c = [])
a.rb:18: syntax error, unexpected `end', expecting end-of-input
on the following code
class A
attr_reader :a, :b, :c
def initialize(a = "default val", b, c = [])
#a = a
#b = b
#c = c
end
def self.open(a)
args = {
b: "2",
c: [1, 2, 3]
}.values
A.new(a, *args)
end
end
when trying to call a property
a2 = A.open("something")
p a2.a
Removing the last default value from initializer =[] solves the problem. Reordering the arguments so the parameters with default values go at the end of initialize helps too.
def initialize(a = "default val", b, c)
or
def initialize(b, c=[], a = "default val") and A.new(*args, a) (but I suppose this is wrong)
As I remember there was a rule about ordering of optional params.
If you define optional parameters before AND after mandatory parameters, in some cases it will be impossible to decide how a goven list or arguments should map to the defined parameters.
In your case, when defining this method:
class A
def initialize(a = "default val", b, c = [])
#...
end
end
How would you handle this when giving two arguments,. i.e.
A.new 'hello', 'world'
You could then assign
a = 'hello'
b = 'world'
c = []
but you could equally set
a = 'default val'
b = 'hello'
c = 'world'
Given this unambiguity, Ruby rejects those constructs. You thus have to define all optional parameters either at the front or the back of your parameter list, while it is commonly accepted standard to define optional arguments only at the end.
If you want to be more specific about which arguments should be set with a large number of optional parameters, you can also use keyword arguments. Since you have to specify the name of the arguments when calling the method here, the order of mandatory and optional keyword arguments doesn't matter.

How can I create an enumerator that does certain things after iteration?

How can I create an enumerator that optionally takes a block? I want some method foo to be called with some arguments and an optional block. If it is called with a block, iteration should happen on the block, and something should be done on it, involving the arguments given. For example, if foo were to take a single array argument, apply map on it with the given block, and return the result of join applied to it, then it would look like:
foo([1, 2, 3]){|e| e * 3}
# => "369"
If it is called without a block, it should return an enumerator, on which instance methods of Enumerator (such as with_index) should be able to apply, and execute the block in the corresponding way:
enum = foo([1, 2, 3])
# => Enumerator
enum.with_index{|e, i| e * i}
# => "026"
I defined foo using a condition to see if a block is given. It is easy to implement the case where the block is given, but the part returning the enumerator is more difficult. I guess I need to implement a sublass MyEnum of Enumerator and make foo return an instance of it:
def foo a, &pr
if pr
a.map(&pr).join
else
MyEnum.new(a)
end
end
class MyEnum < Enumerator
def initialize a
#a = a
...
end
...
end
But calling MyEnum.new raises a warning message: Enumerator.new without a block is deprecated; use Object#to_enum. If I use to_enum, I think it would return a plain Enumerator instance, not the MyEnum with the specific feature built in. On top of that, I am not sure how to implement MyEnum in the first place. How can I implement such enumerator? Or, what is the right way to do this?
You could do something like this.
def foo a, &pr
if pr
a.map(&pr).join
else
o = Object.new
o.instance_variable_set :#a, a
def o.each *y
foo #a.map { |z| yield z, *y } { |e| e }
end
o.to_enum
end
end
Then we have
enum = foo([1,2,3])
enum.each { |x| 2 * x } # "246"
or
enum = foo([1,2,3])
enum.with_index { |x, i| x * i } # "026"
Inspiration was drawn from the Enumerator documentation. Note that all of your expectations about enumerators like you asked for hold, because .to_enum takes care of all that. enum is now a legitimate Enumerator!
enum.class # Enumerator

What are the different possibilities of passing parameters into ruby methods? param/hashlist/array/aproc?

I am trying to understand Ruby in more depth and was reading:
http://www.zenspider.com/Languages/Ruby/QuickRef.html#25
However, I dont understand what the following means in that definition:
parameters := ( [param]* [, hashlist] [*array] [&aProc] )
I know "param" is any number of parameters specified, and then i get lost what the remainder means?
For example, I have:
def doIt(param1, param2, param3)
end
and in this case [param]* is equal to param1, param2, param3...so where does hashlist come in? and *array and &aProc?
Could someone please clarify this for me
If the last argument of a method is a non-empty hash literal, you can pass it like this
def foo(x, y, the_hash)
p the_hash['key2']
end
foo(0, 0, :key1 => 'val1', 'key2' => 42) # 42
instead of the normal way:
foo(0, 0, { :key1 => 'val1', 'key2' => 42 }) # 42
Usually, the hash is defaulted to {} (def foo(x, y, the_hash = {})) so passing an empty hash fits to this scheme.
Additionally, you can specify one "catch-all" (splat) argument which will become an array of all arguments not already assigned to normal arguments:
def foo(p1, *rest)
p rest
end
foo(0) # "[]"
foo(0, 23, 42) # "[23, 42]"
Or, e.g.
def foo(p1, *rest, p2)
p rest
end
foo(0, 100) # "[]"
foo(0, 100, 23, 42) # "[100, 23]"
You cannot have splat arguments before arguments with default value. Therefore, the hash argument syntax and the splat argument are rarely used in combination.
Finally, in your method definition you can have as last argument an identifier prefixed with & which will point to the block at the method invocation (its Proc object) or be nil if there is none. This is normally not needed if you just want to invoke the block -- you can use yield for that.
def foo(&p)
p.call
end
foo { puts "hello" } # hello
vs.
def foo
yield
end
foo { puts "hello" } # hello

Does Hash override Enumerable#map()?

Given that map() is defined by Enumerable, how can Hash#map yield two variables to its block? Does Hash override Enumerable#map()?
Here's a little example, for fun:
ruby-1.9.2-p180 :001 > {"herp" => "derp"}.map{|k,v| k+v}
=> ["herpderp"]
It doesn't override map
Hash.new.method(:map).owner # => Enumerable
It yields two variables which get collected into an array
class Nums
include Enumerable
def each
yield 1
yield 1, 2
yield 3, 4, 5
end
end
Nums.new.to_a # => [1, [1, 2], [3, 4, 5]]
Given that map() is defined by Enumerable, how can Hash#map yield two variables to its block?
It doesn't. It yields a single object to its block, which is a two-element array consisting of the key and the value.
It's just destructuring bind:
def without_destructuring(a, b) end
without_destructuring([1, 2])
# ArgumentError: wrong number of arguments (1 for 2)
def with_destructuring((a, b)) end # Note the extra parentheses
with_destructuring([1, 2])
def with_nested_destructuring((a, (b, c))) p a; p b; p c end
with_nested_destructuring([1, [2, 3]])
# 1
# 2
# 3
# Note the similarity to
a, (b, c) = [1, [2, 3]]
Theoretically, you would have to call map like this:
hsh.map {|(k, v)| ... }
And, in fact, for inject, you actually need to do that:
hsh.inject {|acc, (k, v)| ... }
However, Ruby is more lenient with argument checking for blocks than it is for methods. In particular:
If you yield more than one object, but the block only takes a single argument, all the objects are collected into an array.
If you yield a single object, but the block takes multiple arguments, Ruby performs destructuring bind. (This is the case here.)
If you yield more objects than the block takes arguments, the extra objects get ignored.
If you the block takes more arguments than you are yielding, the extra arguments are bound to nil.
Basically, the same semantics as parallel assignment.
In fact, before Ruby 1.9, block arguments actually did have assignment semantics. This allowed you to do crazy things like this:
class << (a = Object.new); attr_accessor :b end
def wtf; yield 1, 2 end
wtf {|#a, a.b| } # WTF? The block body is empty!
p #a
# 1
p a.b
# 2
This crazy stuff works (in 1.8 and older), because block argument passing is treated the same as assignment. IOW, even though the above block is empty and doesn't do anything, the fact that block arguments are passed as if they had been assigned, means that #a is set and the a.b= setter method is called. Crazy, huh? That's why it was removed in 1.9.
If you want to startle your co-workers, stop defining your setters like this:
attr_writer :foo
and instead define them like this:
define_method(:foo=) {|#foo|}
Just make sure someone else ends up maintaining it :-)

Is there a method in Ruby that does the opposite of find?

a, b, c = 0, 1, 2
[a, b, c].find(&:zero?) # => 0
Is there any method that finds the first element for which the block returns false?
[a, b, c].the_method(&:zero?) # => 1
In other words, it would behave the same way as:
[a, b, c].reject(&:zero?).first
There is not, but you could create one either the clean-ish way:
a = [0,2,1,0,3]
module Enumerable
def first_not(&block)
find{ |x| !block[x] }
end
end
p a.first_not(&:zero?)
#=> 2
...or the horribly-amusing hack way:
class Proc
def !
proc{ |o,*a| !self[o,*a] }
end
end
p a.find(&!(:zero?.to_proc))
#=> 2
...or the terse-but-terribly-dangerous way:
class Symbol
def !
proc{ |o,*a| !o.send(self,*a) }
end
end
p a.find(&!:zero?)
#=> 2
But I'd advocate just skipping the tricky Symbol#to_proc usage and saying what you want:
p a.find{ |i| !i.zero? }
#=> 2
If you're using Ruby 2.0, you may be able to do lazy.reject(&:zero?).first without the performance penalty of going through the full array.
As far as I can tell there is not a standard method to do this (given that find_all and reject reference each other, but find does not reference anything). If you need it frequently (especially if the reject is too slow) you can write your own
module Enumerable
def first_reject(&block)
find {|a| not (block.call(a)) }
end
end

Resources