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

“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

Hi –

On Thu, 25 Oct 2007, Thufir wrote:

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

On 04:50 Thu 25 Oct , Thufir wrote:

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

Thufir wrote:

From my understanding of a hash, you can iterate through the keys only
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

7stud – wrote:

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.

On Thu, 25 Oct 2007 06:14:12 +0900, Ben G. wrote:

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

On Thu, 25 Oct 2007 04:57:50 +0900, David A. Black wrote:

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 D.’ => ‘555-1212’,
‘J. Random Hacker’ => ‘553-1337’
}

phonebook[‘John D.’] 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 D.’ 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

Hi –

On Sat, 27 Oct 2007, Thufir wrote:

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[i]
yield(key, self[key])
i += 1
end
self
end
end

David

On Saturday 27 October 2007 06:57 am, Thufir wrote:

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 K.

On Oct 27, 7:05 am, “David A. Black” [email protected] wrote:

   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* :slight_smile:

David A. Black wrote:

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[i]
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’.

Hi –

On Sun, 28 Oct 2007, Brian A. wrote:

   yield(key, self[key])

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

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

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

David

Hi –

On Sun, 28 Oct 2007, 7stud – wrote:

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?

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

Brian A. wrote:

On Oct 27, 7:05 am, “David A. Black” [email protected] wrote:

   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* :slight_smile:

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

class Hash
alias :orig_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?

On Oct 27, 4:43 pm, 7stud – [email protected] wrote:


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? :slight_smile:

David A. Black wrote:

Hi –

On Sun, 28 Oct 2007, 7stud – wrote:

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

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.

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.

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 :slight_smile:

-Thufir

7stud – wrote:

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…

the block would no longer be accessible
inside the method–only the Proc object assigned to a_block would be
accessible.

Hi –

On Thu, 1 Nov 2007, 7stud – wrote:

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?

accessible. Are you saying that when the next line executes:

def each(&a_block)
No, they’re the same.

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

On Oct 31, 3:16 pm, “David A. Black” [email protected] wrote:

On Thu, 1 Nov 2007, 7stud – wrote:

David A. Black wrote:

On Sun, 28 Oct 2007, 7stud – wrote:

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 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.