Pickaxe question: "... on the way to true Ruby mastery."

J

jeffz_2002

Pickaxe has this code on page 374:

def once(*ids) # :nodoc:
for id in ids
module_eval <<-"end;"
alias_method :__#{id.to_i}__, :#{id.to_s}
private :__#{id.to_i}__
def #{id.to_s}(*args, &block)
(@__#{id.to_i}__ ||= [__#{id.to_i}__(*args, &block)])[0]
end
end;
end
end

followed by "Understand this code, and you'll be well on the way to
true Ruby mastery." I don't, so I'm not, but would like to, and then I
will (according to the book) - with some help.

I'm good up until the line (@__#{id.to_i}__ ||= [__#{id.to_i}__(*args,
&block)])[0]

Say the id.to_i was 42 ... then this reads: (@__42__ ||= [__42__(*args,
&block)])[ 0 ]

or: (@__42__ ||= [ return value from __42__ method call ] )[ 0 ]


Question: Why can't we just write

(@__42__ ||= return value from __42__ method call)

Why is the [0] necessary at all?

Thanks, (ps - a link to an in-depth explanation would be sufficient!)

Jeff
 
J

jeffz_2002

Hey Andre, I believe the line reads slightly differently than you've
interpreted ... some more info (note: I still don't have the answer):

||= is actually a ruby idiom to initialize a variable if it's nil. In
other words, "a ||= 6" actually means "a = 6 if a.nil?" or "a = 6
unless a". (sorry if I'm lecturing) In other words, if @foo really
*is* a value, and it really *is* nil, I think the function call is
going to be made no matter what ... but again, this is a bit over my
head at present.

So, converting __#{id.to_i}__ to something more readable, like "foo",
this: (@foo ||= [foo(*a, &b)] )[ 0 ]

should be the same as the more verbose:

if @foo
return @foo[ 0 ] # Still not sure why the [0] is required!
else
return foo( *a, &b ) # the 0th index of the single array
end

jz
 
D

dblack

Hi --

Hey Andre, I believe the line reads slightly differently than you've
interpreted ... some more info (note: I still don't have the answer):

||= is actually a ruby idiom to initialize a variable if it's nil. In
other words, "a ||= 6" actually means "a = 6 if a.nil?" or "a = 6
unless a". (sorry if I'm lecturing) In other words, if @foo really
*is* a value, and it really *is* nil, I think the function call is
going to be made no matter what ... but again, this is a bit over my
head at present.

So, converting __#{id.to_i}__ to something more readable, like "foo",
this: (@foo ||= [foo(*a, &b)] )[ 0 ]

should be the same as the more verbose:

if @foo
return @foo[ 0 ] # Still not sure why the [0] is required!
else
return foo( *a, &b ) # the 0th index of the single array
end

It's more like:

if @foo
return @foo[0]
else
@foo = [foo(*a, &b)]
return @foo[0]
end

or, more simply,

@foo = [foo(*a, &b)] unless @foo
return @foo[0]

In other words, @foo gets assigned to, if it's nil to start with.


David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
J

jeffz_2002

Thanks David and Andre, I finally get it, and Andre, you're right about
the nil/false idea in your response to my reponse.

For anyone else following this thread, the idea is:

- The first time the method is called, @foo doesn't exist.

- @foo doesn't exist, so it's assigned a single-element array
containing the value returned by the call to the private aliased
original method. This value can be anything, including an array,
false, nil, whatever.

- The 0th array element, which is just the return value of the function
call, is returned.

- The next time the method is called, @foo still exists, and is still a
single-element array. We again return the 0th element, which is the
answer.

The [0] is used because the result is stored in an array ... this gets
around the original method returning nil or false, which would end up
in a second call to the function, had we just naively used @foo ||=
foo( *args, &block )..

Thanks again. It's pretty obvious, actually ... sigh.

Funny, I still don't feel well on my way to mastery. I actually feel
dumber, in a way.

jz
 
D

dblack

Hi --

Thanks again. It's pretty obvious, actually ... sigh.

Funny, I still don't feel well on my way to mastery. I actually feel
dumber, in a way.

I'd say Dave Thomas's characterization of it as non-obvious and
non-trivial is a good reality check in that regard :) And I assure
you that, having studied this example in depth, there's a lot of Ruby
code that you'll take in stride now that you would have found opaque
before. Since you'll take it in stride, you may not *know* that
you're taking it in stride, if you see what I mean -- and that's what
keeps us eager to learn. (And "we" doesn't mean just Ruby programmers
or even just programmers, but people in general who are learning
things.)


David

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
(See what readers are saying! http://www.rubypal.com/r4rrevs.pdf)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
K

Kenosis

Thanks David and Andre, I finally get it, and Andre, you're right about
the nil/false idea in your response to my reponse.

For anyone else following this thread, the idea is:

- The first time the method is called, @foo doesn't exist.

- @foo doesn't exist, so it's assigned a single-element array
containing the value returned by the call to the private aliased
original method. This value can be anything, including an array,
false, nil, whatever.

- The 0th array element, which is just the return value of the function
call, is returned.

- The next time the method is called, @foo still exists, and is still a
single-element array. We again return the 0th element, which is the
answer.

The [0] is used because the result is stored in an array ... this gets
around the original method returning nil or false, which would end up
in a second call to the function, had we just naively used @foo ||=
foo( *args, &block )..

Thanks again. It's pretty obvious, actually ... sigh.

Funny, I still don't feel well on my way to mastery. I actually feel
dumber, in a way.

jz

Welcome to the wonderful world of meta-programming in Ruby! And don't
feel dumb. As the saying goes: the more you know, the more you know
that you don't know. Ask any true master of anything and that'll be
the answer they give you; if they're a >true< master.

Ken

PS. Check these out to continue your mastery...

http://www.erikveen.dds.nl/monitorfunctions/index.html
http://groups.google.com/group/comp...14efc/2627d192d2bf56d5?hl=en#2627d192d2bf56d5
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top