Mike S
Mike S

Reputation: 11409

What order are method arguments evaluated in Ruby?

Up until my attempt to answer this question I had always assumed arguments were evaluated from left to right the way we type them. For example we have some method:

def foo(a,b,c)

Is there a way to monitor variable definitions and values in a Ruby program as time passes? If you call foo 0,1,2 how can you prove that variables assigned in the following order?

time 0: a = 0

time 1: b = 1

time 2: c = 2

I realize my example is trivial because I have the same type of argument for a, b, c but it potentially gets muddied up when you introduce default arguments, keyword arguments, and array arguments. If no one knows the answer I would appreciate suggestions on how to determine the answer.

Basically the Ruby equivalent of this.

Upvotes: 3

Views: 1668

Answers (4)

Gerry
Gerry

Reputation: 11174

For anybody coming here looking for argument assignment order (what the question asks at the time of my post) and not parameter assignment order. I always assumed they were evaluated in the order written until I tried writing some odd code and got a strange result. steenslag gave me an idea of how I could test it:

def arg_test(a, b)
  puts([a, b].inspect '- out of order!') if a > b
end

1000000.times {
  arg_test(Time.now, Time.now)
}

This results in no output. So it seems they are assigned in order and my other code probably just has a bug.

Upvotes: 0

Jörg W Mittag
Jörg W Mittag

Reputation: 369458

The ISO Ruby Language Specification says that arguments are bound to parameters in the order in which they appear in the program text. However, the spec is vague about whether that also means that they are evaluated in that order.

The RubySpec, AFAICS doesn't say anything at all about the evaluation order of method arguments.

So, the answer seems to be: there is no guaranteed evaluation order for method arguments. It may be different between different implementations, it may be different between different versions of the same implementation, it may be different between two runs of the same version of the same implementation, it may even be different between two calls to the same method. They may be evaluated in parallel.

You just don't know.

Upvotes: 5

steenslag
steenslag

Reputation: 80065

def arg_test(a=Time.now, b=Time.now)
  puts "left to right" if a < b
end

arg_test #=> left to right

Upvotes: 3

sawa
sawa

Reputation: 168101

Perhaps you can record the initialization of the objects, and see them later in a stack.

module Record
  @@stack = []
  def initialize *; super; @@stack.push(self) end
  def self.stack; @@stack end
end

class String
  prepend Record
end

def foo a, b, c; end

foo(String.new("x"), String.new("y"), String.new("z"))
Record.stack # => ["x", "y", "z"]

In this case, a, b, c are evaluated in this order.

With default value, you can see:

def foo a, b, c = String.new("c"); end

foo(String.new("x"), String.new("y"))
Record.stack # => ["x", "y", "c"]

Upvotes: 1

Related Questions