
-----------------------------------
wtd
Tue Jul 12, 2005 7:42 pm

Tip: Iterating with an index
-----------------------------------
Let's say we want to iterate over an array and print out each element.  That's pretty easy.

foo = ["hello", "world", "foo", "bar"]

foo.each do |item|
   puts item
end

But, what if you want to iterate with an index number, for instance, so you get the following:

1. hello
2. world
3. foo
4. bar

You might be tempted to write something like: 

foo = ["hello", "world", "foo", "bar"]
index = 1

foo.each do |item|
   puts "#{index}. #{item}"
   index += 1
end

But there's a much easier way, using blocks.

foo = ["hello", "world", "foo", "bar"]

foo.each_with_index do |item, index|
   puts "#{index + 1}. #{item}"
end

-----------------------------------
Cervantes
Wed Jul 13, 2005 7:00 am


-----------------------------------
Nice tip, wtd! :D
This made me think, though, "Does Ruby have overloading?"  It would be nice to not to have to change each to each_with_index, but rather just add a new parameter (?) to the block.  
Or, does Ruby have overloading, just not when it comes to blocks?

-----------------------------------
wtd
Wed Jul 13, 2005 1:19 pm


-----------------------------------
Nice tip, wtd! :D
This made me think, though, "Does Ruby have overloading?"  It would be nice to not to have to change each to each_with_index, but rather just add a new parameter (?) to the block.  
Or, does Ruby have overloading, just not when it comes to blocks?

Ruby doesn't do overloading at all on method names.  For instance, you can't  have:

class Foo
   def bar
      "bar"
   end
   def bar(baz)
      baz
   end
end

However, there certainly are instances where you can make a method work either with or without a block.

The most common way of using blocks is to simply yield values from within a method to a block.

class Foo
   def each 
      for x in ["hello", "world", "foo", "bar"]
         yield x
      end
   end
end

We can then, for instance, simply:

Foo.new.each { |x| puts x }

But in this case we don't really know what the value the block returned.  Where would we need this and how would we get that value?

We'd need it in places like the sort, collect, reject, and select methods.

To accomplish this we need to explicitly pass the block to the method as an argument.  We can do this since a block is just an object of the class Foo
   def each 
      for x in ["hello", "world", "foo", "bar"]
         yield x
      end
   end

   def collect(&block)
      # some code
   end
end

Here we've created the method, and specified the block as a parameter.  Note the ampersand.  As of yet it does nothing.

class Foo
   def initialize
      @a = ["hello", "world", "foo", "bar"]
   end

   def each 
      for x in @a
         yield x
      end
   end

   def collect(&block)
      for x in @a
         # some code
      end
   end
end

Still, it doesn't do anything, but again we have a loop over the array.  How do we call the block with the current array item as its parameter?

class Foo
   def initialize
      @a = ["hello", "world", "foo", "bar"]
   end

   def each 
      for x in @a
         yield x
      end
   end

   def collect(&block)
      for x in @a
         block.call(x)
         # or: block[x]
      end
   end
end

And yet, it's missing something.  We haven't accumulated the answers anywhere.

class Foo
   def initialize
      @a = ["hello", "world", "foo", "bar"]
   end

   def each 
      for x in @a
         yield x
      end
   end

   def collect(&block)
      new_arr = []
      for x in @a
         new_arr.push(block.call(x))
      end
      new_arr
   end
end

There's still a problem.  What if I passed in no block?  

In that case, the block will be set to nil, and we can test for that.

class Foo
   def initialize
      @a = ["hello", "world", "foo", "bar"]
   end

   def each 
      for x in @a
         yield x
      end
   end

   def collect(&block)
      if block.nil?
         @a
      else
         new_arr = []
         for x in @a
            new_arr.push(block.call(x))
         end
         new_arr
      end
   end
end

And now it works as either:

Foo.new.collect

Or:

Foo.new.collect { |x| x.upcase }

-----------------------------------
Cervantes
Wed Jul 13, 2005 9:46 pm


-----------------------------------
Thanks for the explanation on blocks, wtd.  :D

I'm curious about that for loop though:

      for x in @a
         block.call(x)
         # or: block[x]
      end 

I can see that we're iterating through each element in @a, but how exactly does it work?  I would assume that x is an integer that ranges from 0 to 3, rather than the strings (hello, world, foo, and bar).  But then, that doesn't seem to match with your last line of code:
Foo.new.collect { |x| x.upcase }
x must be a string for you to be calling the upcase method.

So, your for loop iterates four times, and the value of x is the particular element of the array.  If I'm correct in my deduction, that's neat. :)

/me loads irb

...

Oh man, that's sweet. :D

I was just checking out the each_with_index is listed as a "mixin".  It also seems to be enumerable.  :think:

-----------------------------------
wtd
Wed Jul 13, 2005 10:12 pm


-----------------------------------
I'm curious about that for loop though:

      for x in @a
         block.call(x)
         # or: block[x]
      end 

I can see that we're iterating through each element in @a, but how exactly does it work?  I would assume that x is an integer that ranges from 0 to 3, rather than the strings (hello, world, foo, and bar).

No, "x" is actually each item in the array, not an index.

So, your for loop iterates four times, and the value of x is the particular element of the array.  If I'm correct in my deduction, that's neat. :)

Correct.

I was just checking out the each_with_index is listed as a "mixin".  It also seems to be enumerable.  :think:

First, define each.

class Bar
   def each 
      for x in 1 .. 4
         yield x
      end
   end
end

Now you can obviously do something meaningless like:

Bar.new.each { |x| puts x }

Now, mix-in enumerable.

class Bar
   include Enumerable
end

Now, try:

Bar.new.each_with_index { |x, i| puts "#{i}. #{x}" }

Or any of the other methods in Enumerable.

The key to a mix-in is understanding that we build on our previous work.  If we have a bunch of methods that all build on "each", for instance, and require nothing else, we can put them in a module that might look like:

module Enumerable
   def each_with_index
      count = 0
      each do |x|
         yield x, count
         count += 1
      end
   end

   # ...
end

Then we can simply include that in a class that defines "each" and get a ton of "free" functionality.

-----------------------------------
Cervantes
Thu Jul 14, 2005 7:51 am


-----------------------------------
Thanks again, wtd. :D
I understand this now.  I like how you included that snippet from the Enumerable module; it's always nice to see the code of the methods that were made for you.

-----------------------------------
wtd
Thu Jul 14, 2005 4:38 pm


-----------------------------------
Thanks again, wtd. :D
I understand this now.  I like how you included that snippet from the Enumerable module; it's always nice to see the code of the methods that were made for you.

Keep in mind that I'm pretty sure Enumerable is written in C.  :)

What I showed you was an approximation.

Of course, you can see how this works by creating your own mix-in.

$ irb
irb(main):001:0> module Nonsense
irb(main):002:1>    def each_with_rand
irb(main):003:2>       each do |item|
irb(main):004:3*          yield item, rand
irb(main):005:3>       end
irb(main):006:2>    end
irb(main):007:1> end
=> nil
irb(main):008:0> class Baz
irb(main):009:1>    def initialize
irb(main):010:2>       @my_array = ["wooble", "ninja", "gah"]
irb(main):011:2>    end
irb(main):012:1>
irb(main):013:1*    def each
irb(main):014:2>       @my_array.each { |item| yield item }
irb(main):015:2>    end
irb(main):016:1>
irb(main):017:1*    include Nonsense
irb(main):018:1> end
=> Baz
irb(main):019:0> Baz.new.each_with_rand do |item, random_number|
irb(main):020:1*    puts "#{item}, #{random_number}"
irb(main):021:1> end
wooble, 0.724940566811711
ninja, 0.0597663172520697
gah, 0.0826400963123888
=> ["wooble", "ninja", "gah"]
irb(main):022:0>


-----------------------------------
Cervantes
Fri Jul 15, 2005 6:55 am


-----------------------------------
Thanks for your insightful replies to the each_with_index tutorial. I understand everything, except I'm a little unclear to the workings of include. 
I assume include acts like copying and pasting everything inside the module to where the include line is. If it were to copy the module itself, you'd have to call it like this,

Baz.new.Nonsense.each_with_rand

right?

Thanks once again, wtd!

-----------------------------------
wtd
Fri Jul 15, 2005 2:07 pm


-----------------------------------
I assume include acts like copying and pasting everything inside the module to where the include line is. If it were to copy the module itself, you'd have to call it like this

If you were to copy and paste the module into the class, you'd access the module as:

Baz::Nonsense

But you wouldn't be able to access the each_with_rand method that way.

irb(main):026:0> class Foo
irb(main):027:1>    module Bar
irb(main):028:2>       def baz
irb(main):029:3>          "Hello world"
irb(main):030:3>       end
irb(main):031:2>    end
irb(main):032:1> end
=> nil
irb(main):033:0> Foo::Bar
=> Foo::Bar
irb(main):034:0> Foo::Bar.baz
NoMethodError: undefined method `baz' for Foo::Bar:Module
        from (irb):34
        from :0
irb(main):035:0> class Foo
irb(main):036:1>    include Bar
irb(main):037:1> end
=> Foo
irb(main):038:0> Foo.new.baz
=> "Hello world"
irb(main):039:0>

So, you see, include is a tad more subtle than just copying and pasting.  :)

-----------------------------------
cahill
Sun Sep 04, 2005 8:15 am


-----------------------------------
What is HTTP proxy server?
HTTP proxy server is a proxy allowing to work on the Internet with HTTP and (not always) FTP protocols. It can carry out caching of information downloaded from the Internet.Now HTTP proxy servers are the most widespread. Their support (ability to use them) is included into many programs: browsers, download managers etc. However, their support is not realized at a level of an operating system ? in order to use them, you should configure all programs, which should use proxies, in an appropriate way. HTTP proxy servers have several anonymity levels. 

http://www.checkproxy.net
