scope of assignments with lambda

F

Fearless Fool

This isn't a question -- it's just a comment that I find it awesome and
only slightly mysterious that a lambda can reference the variable it is
assigned to in its body, such as p here:
bing 5
bing 4
bing 3
bing 2
bing 1
bing 0
=> nil

I guess that's the power of a real lexical closure. Very handy.
 
R

Ruby User

Fearless Fool wrote in post #988575:
This isn't a question -- it's just a comment that I find it awesome and
only slightly mysterious that a lambda can reference the variable it is
assigned to in its body, such as p here:

bing 5
bing 4
bing 3
bing 2
bing 1
bing 0
=> nil

I guess that's the power of a real lexical closure. Very handy.

For a real eye-opener, see this discussion from 2007.
https://groups.google.com/d/topic/comp.lang.ruby/OlHsBJoud_0/discussion
 
7

7stud --

Fearless Fool wrote in post #988575:
This isn't a question -- it's just a comment that I find it awesome and
only slightly mysterious that a lambda can reference the variable it is
assigned to in its body,

How does recursion grab you?

def factorial(n)
if n == 1
1
else
n * factorial(n-1)
end
end

puts factorial(3)

--output:--
6


You might want to ponder the difference between your example and a
recursive method.
 
F

Fearless Fool

7stud -- wrote in post #988636:
How do recursive methods grab you?
...
You might want to ponder the difference between your example and a
recursive method.

@7stud: Pardon if I wasn't clear in my OP, but I was asking about
recursion. [For the record, Lisp was one of my first programming
languages, and I often find it easier to think in terms of recursion
than in iteration. I'd be more impressed if you wrote your factorial
routine as a lazy-eval'd stream, ala Abelson & Sussman SICP. ;)]

What I was surprised about was the scoping of variables in assignment
statements. I guess it makes sense. Assume that p is previously
undefined, then a reference on the RHS to p like this:

p = p + 1

will raise an error. But wrapped inside a lambda, the reference to p is
deferred, so it all works:

p = lambda { |n| p.call(n-1) if (n > 0) }

As I said, that's the power of a lexical closure.
 
M

Markus Fischer

Ruby User wrote in post #988632:

Thanks. Here's an alternative link, for those who have trouble with the
above:

Thanks that you mention it, I was able to see that groups was loading
the page but then replaced it's content shortly afterwards.

- Markus
 
7

7stud --

Fearless Fool wrote in post #988777:
7stud -- wrote in post #988636:

@7stud: Pardon if I wasn't clear in my OP, but I was asking about
recursion.

My point was that a recursive method seems harder to explain than your
lambda example. As you detailed, your lambda example can be explained
by a closure.

In the case of a recursive method, how do you explain being able to call
a name that doesn't even exist yet? Is it because what's really
happening is something like this:

factorial = Method.new(Object) do
if n == 1
1
else
n * factorial(n-1)
end
end

...and therefore a closure can explain why it works? In ruby, you can
certainly do something this:

MyClass = Class.new do
def greet
say 'hi'
end
end

MyClass.new.greet

--output:--
hi
 
F

Fearless Fool

7stud -- wrote in post #988797:
My point was that a recursive method seems harder to explain than your
lambda example. As you detailed, your lambda example can be explained
by a closure.

Yeah, well, I apologize, but that was my typo. I meant "I *wasn't*
asking about recursion". (And no, recursion doesn't seem particularly
difficult to explain, does it?!? :)

- ff
 
K

Kevin Mahler

Fearless Fool wrote in post #988777:
What I was surprised about was the scoping of variables in assignment
statements. I guess it makes sense. Assume that p is previously
undefined, then a reference on the RHS to p like this:

p = p + 1

will raise an error. But wrapped inside a lambda, the reference to p is
deferred, so it all works:

p = lambda { |n| p.call(n-1) if (n > 0) }

As I said, that's the power of a lexical closure.

"The scoping of variables in assignment" is exactly the same in both
cases.

p = p + 1 does not fail because p is undefined. p is defined. Its
value is nil. The error results from NilClass#+ not existing. This has
nothing to do with "the power of the lexical closure".

Local variables are discovered at compile time. p gets defined at the
beginning of the scope, *before* the line "p = p + 1", and it's
assigned the value of nil. Try

p = p.to_i + 1

and observe the non-error. Even better, try

def foo
puts local_variables #=>bar
bar = 99
end

The reference to p inside the lambda is not deferred. At compile-time,
the p inside the lambda is bound to the local variable p, just as the
p on the right-hand side of "p = p + 1" is bound to the local variable
p. There is nothing special going on as a result of the lambda being
present.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top