Closure question

I'm trying to make a function act as a counter, but I can't seem to get
it to work. Here's what I've tried:

def count
  counter = 1
  def count
    proc {counter += 1}.call
  end
  counter
end

I was hoping for this:
count => 1
count => 2
count => 3

Instead, I got this:
count => 1
count => NoMethodError: undefined method '+' for nil:NilClass

Isn't the idea of a closure that everything in the block is executed in
the same scope it was defined? It seems that the name 'counter' is
referenced under the name 'count', and so when I redefine 'count', the
'counter' variable gets lost. Anyone know what's really happening here?

Thanks,
Greg

CONFIDENTIALITY NOTICE

This message and any included attachments
are from Cerner Corporation and are intended
only for the addressee. The information
contained in this message is confidential and
may constitute inside or non-public information
under international, federal, or state
securities laws. Unauthorized forwarding,
printing, copying, distribution, or use of such
information is strictly prohibited and may be
unlawful. If you are not the addressee, please
promptly delete this message and notify the
sender of the delivery error by e-mail or you
may call Cerner's corporate offices in Kansas
City, Missouri, U.S.A at (+1) (816)221-1024.
---------------------------------------- --

Fitzgerald,Greg wrote:

I'm trying to make a function act as a counter, but I can't seem to get
it to work. Here's what I've tried:

def count
  counter = 1
  def count
    proc {counter += 1}.call
  end
  counter
end

def create_count
   counter = 0
   return proc { counter += 1 }
end

counter = create_count

counter.call # => 1
counter.call # => 2
counter.call # => 3

Regards,

   Michael

Re-defining the method 'count' within its own scope won't really help;
you could do some sort of undef/def swap, but I think that's more
complexity than you really need.

To emulate an anonymous closure with no associated object instance in
Ruby, you need to use a 'proc' object bound in a context where your
counter variable is in scope.

For example:

···

---
def make_counter
  i = 0
  proc { i+=1 }
end

c = make_counter
c.call
=> 1
c[] # alias for '.call'
=> 2

d = make_counter
d[]
=> 1
---
Does that help?

Lennon

Fitzgerald,Greg said:

I'm trying to make a function act as a counter, but I can't seem to get
it to work. Here's what I've tried:

[... example elided ...]

Isn't the idea of a closure that everything in the block is executed in
the same scope it was defined? It seems that the name 'counter' is
referenced under the name 'count', and so when I redefine 'count', the
'counter' variable gets lost. Anyone know what's really happening here?

The proc (i.e. closure) doesn't get defined until the second time count is
called. By that time, the counter variable is long out of scope (the body
of a "def" does not form a closure, so the counter variable is not in
scope when the proc is evaluated).

The traditional way to approach this is ...

  def make_counter
    counter = 0
    proc { counter += 1 }
  end

  c = make_counter
  c.call # => 1
  c.call # => 2

But that doesn't make a function look like a counter as you were attempting.

···

--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

Fitzgerald,Greg schrieb:

I'm trying to make a function act as a counter, but I can't seem to get
it to work. Here's what I've tried:

def count
  counter = 1
  def count
    proc {counter += 1}.call
  end
  counter
end

I was hoping for this:
count => 1
count => 2
count => 3

Instead, I got this:
count => 1
count => NoMethodError: undefined method '+' for nil:NilClass

Hi Greg,

the inner def creates a new scope, so you can't access the counter variable from there. Using define_method, you can avoid creating a new scope:

def count
   counter = 1
   self.class.send :define_method, :count do counter += 1 end
   counter
end

p count # => 1
p count # => 2
p count # => 3

Regards,
Pit

In article <25820D5115DEEB48AC514130CA7BA29355F85A@MSMBWHQ05.northamerica.cerner.net>,

------_=_NextPart_001_01C4798D.94BA4233
Content-Type: text/plain;
charset="us-ascii"
Content-Transfer-Encoding: quoted-printable

I'm trying to make a function act as a counter, but I can't seem to get
it to work. Here's what I've tried:

def count
counter =3D 1
def count
   proc {counter +=3D 1}.call
end
counter
end

I was hoping for this:
count =3D> 1
count =3D> 2
count =3D> 3

Instead, I got this:
count =3D> 1
count =3D> NoMethodError: undefined method '+' for nil:NilClass

Isn't the idea of a closure that everything in the block is executed in
the same scope it was defined? It seems that the name 'counter' is
referenced under the name 'count', and so when I redefine 'count', the
'counter' variable gets lost. Anyone know what's really happening here?

The problem is that you've defined a new scope with the definition of the
inner 'count' method, so the 'counter' variable you're referring to inside
of the proc is not the same as the one defined in the outer count method.
Also, everytime you call 'count', doesn't the 'counter' variable get
reinitialized to '1'? And, it's not clear to me how the inner 'count'
method would ever get called. I believe you're seeing the error because
both the inner and the outer 'count' methods are both called 'count'.

I modified your code somewhat to remove the name collision:

irb(main):064:0> def foo
irb(main):065:1> counter = 1
irb(main):066:1> def bar
irb(main):067:2> proc {counter += 1}.call
irb(main):068:2> end
irb(main):069:1> bar #-> note this added call to bar
irb(main):070:1> counter
irb(main):071:1> end
=> nil
irb(main):072:0> foo
NoMethodError: undefined method `+' for nil:NilClass
  from (irb):67:in `bar'
  from (irb):67:in `call'
  from (irb):67:in `bar'
  from (irb):69:in `foo'
  from (irb):72

.... so the inner method, bar, knows nothing about the 'counter' variable
defined in the outer scope.

Here's how I'd do a counter, but it doesn't involve closures, so maybe it
doesn't address your question:

class Counter
  def initialize
    @counter = 1
  end
  
  def count
    @counter += 1
  end
end

cc = Counter.new
cc.count #=> 2
cc.count #=> 3

Phil

···

Fitzgerald,Greg <GFITZGERALD@CERNER.COM> wrote:
  from :0

Jim Weirich said:

The proc (i.e. closure) doesn't get defined until the second time count is
called. By that time, the counter variable is long out of scope (the body
of a "def" does not form a closure, so the counter variable is not in
scope when the proc is evaluated).

So the obvious answer is: Don't use a "def" to redefine count. Use
"define_method" which takes a closure/block as the method body.

def count
  counter = 1
  self.class.send(:define_method, :count) { counter += 1 }
  counter
end

p count #=> 1
p count #=> 2
p count #=> 3

···

--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)