最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Capturing variables in Ruby methods - Stack Overflow

programmeradmin10浏览0评论

In CoffeeScript:

f = ->
  v = 5
  g = ->
    v
  g()

f() # returns 5 as expected

In Ruby:

def f
  v = 5
  def g
    v # undefined local variable or method `v' for main:Object (NameError)
  end
  g
end
f

Okay, so apparently JavaScript functions are set up to capture variables in the scope they are created in, but Ruby's methods are not. Is there a way to make Ruby methods behave like JavaScript functions in this regard?

In CoffeeScript:

f = ->
  v = 5
  g = ->
    v
  g()

f() # returns 5 as expected

In Ruby:

def f
  v = 5
  def g
    v # undefined local variable or method `v' for main:Object (NameError)
  end
  g
end
f

Okay, so apparently JavaScript functions are set up to capture variables in the scope they are created in, but Ruby's methods are not. Is there a way to make Ruby methods behave like JavaScript functions in this regard?

Share Improve this question edited Aug 3, 2013 at 0:59 Michael Francis asked Aug 2, 2013 at 23:14 Michael FrancisMichael Francis 8,74712 gold badges47 silver badges74 bronze badges 3
  • 1 Ruby doesn't have functions. It has methods and it has blocks. Methods have new scopes, blocks have nested scopes. – Jörg W Mittag Commented Aug 3, 2013 at 0:55
  • @JörgWMittag Actually, as I learned from Fred's answer, Ruby has methods and closures. Blocks are just one of three kinds of closures in Ruby. There are also Lambdas and Procs. – Michael Francis Commented Aug 3, 2013 at 1:02
  • 3 Lambdas and Procs are just objects which are created by passing blocks to methods. (Or using the lambda literal syntax.) – Jörg W Mittag Commented Aug 3, 2013 at 1:11
Add a comment  | 

5 Answers 5

Reset to default 9

Ruby has script scope, module/class/method definition scope and block scope. Only blocks create nested scopes. So, you need to use a block to define your method. Thankfully, there is a method for defining methods that takes a block:

def f
  v = 5
  define_method :g do
    v
  end
  g
end
f
# => 5

However, note that this does not do what you think it does (and neither does your original code). It does not define a method g nested in method f. Ruby doesn't have nested methods. Methods always belong to modules (classes are modules), they cannot belong to methods.

What this does is define a method f, which when you run it defines a method g and then calls that method.

Observe:

methods.include?(:g)
# => true

You have now defined a new top-level method (actually a private instance method of Object) called g, and you will define it over and over and over again, everytime f gets called.

What you probably want is a lambda:

def f
  v = 5
  g = -> { v }
  g.()
end
f
# => 5

In a comment to another answer you wrote:

Okay, so I'm messing around with lambdas, and I noticed that anything I do to v inside of g is not reflected in v once g returns. Is there a way I can make my changes to v stick?

def f
  v = 5
  g = -> { v = 'Hello' }
  g.()
  v
end
f
# => 'Hello'

As you can see, the change to v does "stick" after g returns.

You don't really define methods inside of methods in Ruby, but you can use lambda:

def f
  v = 5

  g = lambda do
    v
  end

  g.call
end

Short answer, no. Ruby's functions have different scoping rules than Javascript functions.

Longer answer is that you can define objects in Ruby that preserve the scope in which they are defined (called a closure). These are called lambdas, Procs, and blocks in Ruby. Look here for a quick overview.

Edit: blocks are not Ruby objects, but they can be converted into Procs, which are objects.

To add to the other answers, for most consistency with your CoffeeScript code, you would probably write:

f = lambda do
  v = 5

  g = lambda do
    v
  end

  g[]
end
f[]

A closure is a named block of code (a function in some languages, a lambda or proc in Ruby) with the following characteristics:

  • It remembers the values of all variables that were available in the scope it was defined (outer scope), even when called on another scope or when those variables are no longer available (out of scope).
  • It is an object. Hence, it can be assigned to a variable, passed around, called from within different scopes, etc.

Below is an example using a Lambda. The same could be done using a Proc.

minutes = 40
def meditate minutes
    return lambda { minutes }
end

p = meditate minutes
minutes = nil
p.call

# Output:
=> 40

Notice the lambda was created inside the meditate method, which took minutes as an argument. Later, when we called the lambda, the meditate method was already gone. We also set the value of minutes to nil. Nonetheless, the lambda remembered the value of "minutes".

Ruby has two types of closures: Lambdas and Procs.

Methods are not closures, neither are the Method objects. As stated by Yukihiro Matsumoto (Matz) in his book The Ruby Programming Language:

One important difference between Method objects and Proc objects is that Method objects are not closures. Ruby's method are intended to be completely self-contained, and they never have access to local variables outside of their scope. The only binding retained by a Method object, therefore, is the value of self - the object on which the method is to be invoked.

See more at this post about Methods in the Zen Ruby blog.

发布评论

评论列表(0)

  1. 暂无评论