[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

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

Thufir Hawat

10/24/2007 7:47:00 PM

"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

31 Answers

David A. Black

10/24/2007 7:58:00 PM

0

Ben Giddings

10/24/2007 9:14:00 PM

0

On 04:50 Thu 25 Oct , Thufir wrote:
> "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

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


7stud --

10/25/2007 12:11:00 AM

0

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
--
Posted via http://www.ruby-....

7stud --

10/25/2007 12:22:00 AM

0

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.
--
Posted via http://www.ruby-....

Thufir Hawat

10/27/2007 10:50:00 AM

0

Thufir Hawat

10/27/2007 10:58:00 AM

0

David A. Black

10/27/2007 11:06:00 AM

0

Randy Kramer

10/27/2007 11:36:00 AM

0

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 Kramer

7stud --

10/27/2007 6:13:00 PM

0

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'.
--
Posted via http://www.ruby-....

Brian Adkins

10/27/2007 7:03:00 PM

0

On Oct 27, 7:05 am, "David A. Black" <dbl...@rubypal.com> 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

Or this:

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

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