.each do |foo, bar| what does bar do?

T

Thufir

"code_words.each do |real, code|
idea.gsub!( real, code )
end
You see the each method? The each method is all over in Ruby. It's
available for Arrays, Hashes, even Strings. Here, our code_words
dictionary is kept in a Hash. This each method will hurry through all
the pairs of the Hash, one dangerous word matched with its code word,
handing each pair to the gsub! method for the actual replacement."

from page 33 of whys-poignant-guide-to-ruby.pdf



Is this similar to nested for statements? I don't think so. In the
first line, why are both "real" and "code" part of the interation?
From my understanding of a hash, you can iterate through the keys only
and then find the corresponding bit of the hash.

Why would this fail:

code_words.each do |real|
idea.gsub!( real, code )
end

wouldn't the corresponding code get looked up by during the loop? Or,
how could the above be changed so that it would work?




thanks,

Thufir
 
D

David A. Black

Hi --

"code_words.each do |real, code|
idea.gsub!( real, code )
end
You see the each method? The each method is all over in Ruby. It's
available for Arrays, Hashes, even Strings. Here, our code_words
dictionary is kept in a Hash. This each method will hurry through all
the pairs of the Hash, one dangerous word matched with its code word,
handing each pair to the gsub! method for the actual replacement."

from page 33 of whys-poignant-guide-to-ruby.pdf



Is this similar to nested for statements? I don't think so. In the
first line, why are both "real" and "code" part of the interation?
and then find the corresponding bit of the hash.

Why would this fail:

code_words.each do |real|
idea.gsub!( real, code )
end

wouldn't the corresponding code get looked up by during the loop? Or,
how could the above be changed so that it would work?

Hashes yield key,value pairs to #each. So you need two block params to
pick them up.

If you just want the keys, you can use #each_key.


David

--
Upcoming training by David A. Black/Ruby Power and Light, LLC:
* Advancing With Rails, Edison, NJ, November 6-9
* Advancing With Rails, Berlin, Germany, November 19-22
* Intro to Rails, London, UK, December 3-6 (by Skills Matter)
See http://www.rubypal.com for details!
 
B

Ben Giddings

"code_words.each do |real, code|
idea.gsub!( real, code )
end
You see the each method? The each method is all over in Ruby. It's
available for Arrays, Hashes, even Strings. Here, our code_words
dictionary is kept in a Hash. This each method will hurry through all
the pairs of the Hash, one dangerous word matched with its code word,
handing each pair to the gsub! method for the actual replacement."

from page 33 of whys-poignant-guide-to-ruby.pdf



Is this similar to nested for statements? I don't think so. In the
first line, why are both "real" and "code" part of the interation?
and then find the corresponding bit of the hash.

Why would this fail:

code_words.each do |real|
idea.gsub!( real, code )
end

What's happening in the first version of the code is that "real" and
"code" are being assigned from within the "each" method. At some
point within each there's some code that essentially looks like:
"yield(current_hash_key, current_hash_value)". When that code is run,
ruby assigns the variable (in your scope) real to the value of
"current_hash_key" within the "each" method, and it assigns the
variable "code" to the value of "current_hash_value".

Because "yield" has two arguments, the block you pass each should have
two parameters, which it does. If you used this instead:

code_words.each do |foo|
...
end

Foo would be assigned an array containing both things sent by "yield",
i.e. foo[0] would be the same as real, and foo[1] would be the same as
code.

The key thing here is that not every "each" is the same. Some have a
"yield" that tries to send out one value (like the "each" for arrays),
some pass multiple values (like the "each" for hashes). You need to
know how many variables your "each" wants to assign in a block.

In your example:

code_words.each do |real|
idea.gsub!( real, code )
end

real would get assigned, but "code" wouldn't have been assigned, so
Ruby wouldn't know what "code" was and would complain.

Ben
 
7

7stud --

Thufir said:
and then find the corresponding bit of the hash.

Your understanding is incorrect.
in the
first line, why are both "real" and "code" part of the interation?

Ok, let's get some preliminaries straight:

arr = [1, 2]
a, b = arr
puts a,b

--output:--
1
2

That's a form of what's called 'parallel assignment' in ruby.

The each() method for a hash sends an array consisting of a key/value
pair to a block:

h = {"a"=>1, "b"=>2}

h.each do |arr|
p arr
end

--output:--
["a", 1]
["b", 2]


The output shows that each() *assigns* an array to the parameter
variable 'arr'. Earlier it was established that parallel assignment can
be used with arrays. So that loop can also be written like this:

h = {"a"=>1, "b"=>2}

h.each do |key, val|
print key, val
puts
end


--output:--
a1
b2

As you can see from the output, ruby is perfectly happy to do parallel
assignment when passing that array to the block.

Why would this fail:

code_words.each do |real|
idea.gsub!( real, code )
end

For the same reason the following program will fail:

puts code

wouldn't the corresponding code get looked up by during the loop?

How? In the first instance, you say that it's your understanding that
when examining a hash with each(), each() will only produce the keys,
but then you ask why 'code', which is a value, isn't looked up during
the loop. So, what exactly is your understanding?
how could the above be changed so that it would work?

code_words.each do |arr|
idea.gsub!( arr[0], arr[1] )
end
 
7

7stud --

7stud said:
As you can see from the output, ruby is perfectly happy to do parallel
assignment when passing that array to the block.

That should say:

As you can see from the output, ruby is perfectly happy to do parallel
assignment when passing an array to a block.
 
T

Thufir

Hashes yield key,value pairs to #each. So you need two block params to
pick them up.

If you just want the keys, you can use #each_key.


Ok, I went and read <http://en.wikipedia.org/wiki/Associative_array#Ruby>
which helped, if only by confirming the syntax. They give the example:

phonebook = {
'Sally Smart' => '555-9999',
'John Doe' => '555-1212',
'J. Random Hacker' => '553-1337'
}

phonebook['John Doe'] produces '555-1212'

To iterate over the hash, use something like the following:

phonebook.each {|key, value| puts key + " => " + value}


But, what's being iterated through, the keys or the values? To my
understanding, each key must be unique and will lookup or map to a
specific value. (I'm thinking of the keys as a list, in that there
cannot be duplicate keys. There can be only one 'John Doe' in the above
to my understanding, but many others could have the same value for the
phone number field.)

The keys can be iterated through, and that's all that required to get the
entire hash map because the values can be looked up from the key field.
So, why are both the key and value iterated through?

I suppose it's kinda "because". As you said, key value pairs are yielded
to #hash, so both key and value must be passed as parameters. Again,
though, that doesn't seem strictly required (that both are passed). Why
is it required?

It certainly seems possible to my mind to pass a key and get back both
key and value. It's then clear what's being iterated through: the key
field. If both key and field are passed, it's unclear what's being
iterated through.


thanks,

Thufir
 
T

Thufir

Because "yield" has two arguments, the block you pass each should have
two parameters, which it does. If you used this instead:

code_words.each do |foo|
...
end

Foo would be assigned an array containing both things sent by "yield",
i.e. foo[0] would be the same as real, and foo[1] would be the same as
code.


I don't see what's wrong with having foo[0] and foo[1], but, yes, it's
clearer to instead use foo and bar instead. Ok, that convinces me that
it's good to do it this way, thank you.

I just can't wrap my mind around *what's* being iterated through. I
guess it's the way that, as you put it, the "yield(current_hash_key,
current_hash_value)" is happening behind the scenes.


-Thufir
 
D

David A. Black

Hi --

Because "yield" has two arguments, the block you pass each should have
two parameters, which it does. If you used this instead:

code_words.each do |foo|
...
end

Foo would be assigned an array containing both things sent by "yield",
i.e. foo[0] would be the same as real, and foo[1] would be the same as
code.


I don't see what's wrong with having foo[0] and foo[1], but, yes, it's
clearer to instead use foo and bar instead. Ok, that convinces me that
it's good to do it this way, thank you.

I just can't wrap my mind around *what's* being iterated through. I
guess it's the way that, as you put it, the "yield(current_hash_key,
current_hash_value)" is happening behind the scenes.

Yes, that's correct. yield is a keyword that acts like a method call;
it can have multiple arguments. Blocks can have multiple parameters,
and can therefore be called with multiple arguments.

If you were going to write Hash#each in Ruby, without recourse to
#each, you could write it like this:

class Hash
def each
keys = self.keys # make a 'keys' local variable
i = 0
until i == keys.size
key = keys
yield(key, self[key])
i += 1
end
self
end
end


David

--
Upcoming training by David A. Black/Ruby Power and Light, LLC:
* Advancing With Rails, Edison, NJ, November 6-9
* Advancing With Rails, Berlin, Germany, November 19-22
* Intro to Rails, London, UK, December 3-6 (by Skills Matter)
See http://www.rubypal.com for details!
 
R

Randy Kramer

I just can't wrap my mind around *what's* being iterated through. I
guess it's the way that, as you put it, the "yield(current_hash_key,
current_hash_value)" is happening behind the scenes.

Well, let's look at an example hash:

{ "key1"=>"value1", "key2"=>"value2", "cow"=>"bovine", 12=>"dodecine" }

A hash (literal) is "a list of key => value pairs between braces" (from
pickaxe2).

When you iterate through the hash, you are iterating through the key=>value
*pairs*--on the first iteration you get the values "key1"=>"value1", on the
2nd iteration you get "key2"=>"value2", and so on. Note that you get *two*
values.

Randy Kramer
 
7

7stud --

David said:
If you were going to write Hash#each in Ruby, without recourse to
#each, you could write it like this:

class Hash
def each
keys = self.keys # make a 'keys' local variable
i = 0
until i == keys.size
key = keys
yield(key, self[key])
i += 1
end
self
end
end


David


A test run:

h = {"a"=>1, "b"=>2}
h.each do |arr|
p arr
end

--output:--
r5test.rb:2: warning: method redefined; discarding old each
r5test.rb:15: warning: multiple values for a block parameter (2 for 1)
from r5test.rb:7
["a", 1]
r5test.rb:15: warning: multiple values for a block parameter (2 for 1)
from r5test.rb:7
["b", 2]

7stud -- wrote:

The each() method for a hash sends an array consisting of a key/value
pair to a block:

h = {"a"=>1, "b"=>2}

h.each do |arr|
p arr
end

--output:--
["a", 1]
["b", 2]


The output shows that each() *assigns* an array to the parameter
variable 'arr'.
 
B

Brian Adkins

If you were going to write Hash#each in Ruby, without recourse to
#each, you could write it like this:

class Hash
def each
keys = self.keys # make a 'keys' local variable
i = 0
until i == keys.size
key = keys
yield(key, self[key])
i += 1
end
self
end
end


Or this:

class Hash
def each
each_key {|key| yield key, self[key] }
end
end

I know, you meant w/o recourse to each* :)
 
D

David A. Black

Hi --

If you were going to write Hash#each in Ruby, without recourse to
#each, you could write it like this:

class Hash
def each
keys = self.keys # make a 'keys' local variable
i = 0
until i == keys.size
key = keys
yield(key, self[key])
i += 1
end
self
end
end


Or this:

class Hash
def each
each_key {|key| yield key, self[key] }
end
end

I know, you meant w/o recourse to each* :)


Yes -- I don't think each_key uses each but it's definitely out of
bounds for my example :) Mainly, of course, I wanted to "explode" the
whole thing so that the underlying logic was brought to the surface.


David

--
Upcoming training by David A. Black/Ruby Power and Light, LLC:
* Advancing With Rails, Edison, NJ, November 6-9
* Advancing With Rails, Berlin, Germany, November 19-22
* Intro to Rails, London, UK, December 3-6 (by Skills Matter)
See http://www.rubypal.com for details!
 
7

7stud --

Brian said:
i += 1
end
self
end
end

Or this:

class Hash
def each
each_key {|key| yield key, self[key] }
end
end


That suffers the same problem as David Black's example.

I know, you meant w/o recourse to each* :)

My tests show that each_keys() does not call Hash#each(), so your
example seems to use fair means to me:

class Hash
alias :eek:rig_each :each

def each(&block)
orig_each(&block)
puts "orig each called"
end

def my_method
each_key {|key| yield key, self[key] }
end
end

h = {"a"=>1, "b"=>2}

#call original each() method for a hash:
h.each do |key, val|
print key, " ", val
puts
end

puts

#call a method that uses each_key():
h.my_method do |key, val|
print key, " ", val
puts
end


--output:--
a 1
b 2
orig each called

a 1
b 2

Note that in the last output Hash#each() wasn't called.

That example has raised a question of my own. Instead of having to
write:

def each(&block)
orig_each(&block)

why can't I relay the block to orig_each() without the second '&', like
this

def each(&block)
orig_each(block)

According to pickaxe2, p56, the '&' method converts the specified block
to a Proc object and assigns it to the parameter variable 'block'. Why
is the second call to '&' required?
 
D

David A. Black

Hi --

Brian said:
i += 1
end
self
end
end

Or this:

class Hash
def each
each_key {|key| yield key, self[key] }
end
end


That suffers the same problem as David Black's example.

What problem did mine suffer from?
That example has raised a question of my own. Instead of having to
write:

def each(&block)
orig_each(&block)

why can't I relay the block to orig_each() without the second '&', like
this

def each(&block)
orig_each(block)

According to pickaxe2, p56, the '&' method converts the specified block
to a Proc object and assigns it to the parameter variable 'block'. Why
is the second call to '&' required?

Because there's a difference between passing a Proc around as an
object, and supplying a code block to a method. You can do both:

meth(arg,&block)

and arg can be a Proc object. So there has to be some way to tell the
method what you're doing.


David

--
Upcoming training by David A. Black/Ruby Power and Light, LLC:
* Advancing With Rails, Edison, NJ, November 6-9
* Advancing With Rails, Berlin, Germany, November 19-22
* Intro to Rails, London, UK, December 3-6 (by Skills Matter)
See http://www.rubypal.com for details!
 
B

Brian Adkins

...
That suffers the same problem as David Black's example.
...
My tests show that each_keys() does not call Hash#each(), so your
example seems to use fair means to me:

You're a literal type of person, aren't you? :)
 
T

Thufir

I just found an explanation of hashes which is easier for me to grab
onto:

Arrays and Hashes

Ruby's arrays and hashes are indexed collections. Both store
collections of objects, accessible using a key. With arrays, the key
is an integer, whereas hashes support any object as a key. Both arrays
and hashes grow as needed to hold new elements.

<http://www.ruby-doc.org/docs/ProgrammingRuby/>


Just thought I'd throw that out there :)



-Thufir
 
7

7stud --

David said:
Hi --

class Hash
def each
each_key {|key| yield key, self[key] }
end
end


That suffers the same problem as David Black's example.

What problem did mine suffer from?

It doesn't return an array.
def each(&block)
orig_each(&block)

versus


Because there's a difference between passing a Proc around as an
object, and supplying a code block to a method. You can do both:

meth(arg,&block)

and arg can be a Proc object. So there has to be some way to tell the
method what you're doing.

I'm not getting it. With this definition:

def each(&a_block)

when I call:

each() {some block}

ruby converts the block to a Proc object and assigns it to the variable
a_block. So, it seems to me that after ruby passes the args specified
in the method call to each(), the block would no longer be accessible
inside the method--only the Proc object assigned to a_block would be
accessible. Are you saying that when the next line executes:
def each(&a_block)
orig_each(&a_block) <----****

that the parameter variable a_block in the line:
orig_each(&a_block)

is not the same variable as the a_block in the line:
def each(&a_block)

???? In other words, does a_block in the line:

orig_each(&a_block)


reach outside the method definition and reference the block that is
floating around in the ether? Does ruby re-convert the block into a
Proc object and re-assigns the Proc object to a_block? If not, I don't
understand why the second '&' is necessary: writing a_block should be
enough to access the Proc object that ruby assigned to the parameter
variable a_block when the method was first called.
 
7

7stud --

7stud said:
I'm not getting it. With this definition:

def each(&a_block)

when I call:

each() {some block}

ruby converts the block to a Proc object and assigns it to the variable
a_block. So, it seems to me that after ruby passes the args specified
in the method call to each()...

Whoops. That doesn't make any sense. That should say something like:

After ruby gathers up the block, converts it to a Proc object, and
assigns the Proc object to the parameter variable a_block...
 
D

David A. Black

Hi --

David said:
Hi --

class Hash
def each
each_key {|key| yield key, self[key] }
end
end



That suffers the same problem as David Black's example.

What problem did mine suffer from?

It doesn't return an array.

Was it supposed to?
I'm not getting it. With this definition:

def each(&a_block)

when I call:

each() {some block}

ruby converts the block to a Proc object and assigns it to the variable
a_block. So, it seems to me that after ruby passes the args specified
in the method call to each(), the block would no longer be accessible
inside the method--only the Proc object assigned to a_block would be
accessible. Are you saying that when the next line executes:


that the parameter variable a_block in the line:


is not the same variable as the a_block in the line:

No, they're the same.
???? In other words, does a_block in the line:




reach outside the method definition and reference the block that is
floating around in the ether? Does ruby re-convert the block into a
Proc object and re-assigns the Proc object to a_block? If not, I don't
understand why the second '&' is necessary: writing a_block should be
enough to access the Proc object that ruby assigned to the parameter
variable a_block when the method was first called.

It does access the Proc object. But accessing the Proc object is only
part of the story; there's also the question of what it does with the
Proc object.

If you do this:

some_method(some_proc_object)

you're just passing the Proc object around the same way you would pass
a string or an array or any other object.

If you do this:

some_method &some_proc_object

you're telling some_method that you want some_proc_object to play the
special role of code-block.

There could even be a situation where you would do:

some_method(proc_1, proc_2) &proc_3

i.e., send two procs as regular arguments, and use a third one as the
code block.


David

--
Upcoming training by David A. Black/Ruby Power and Light, LLC:
* Advancing With Rails, Edison, NJ, November 6-9
* Advancing With Rails, Berlin, Germany, November 19-22
* Intro to Rails, London, UK, December 3-6 (by Skills Matter)
See http://www.rubypal.com for details!
 
B

Brian Adkins

Was it supposed to?

I think so, if you want to avoid the "multiple value" warnings shown
in the earlier post. I suppose someone could look at the Ruby source
to see if that's what it's actually doing, but I suspect there is a
good chance it is, judging from the behavior. However, I think that
would've just cluttered up the example since it's not germane to your
purpose.
 

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

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top