Programming C, C++, Java, PHP, Ruby, Turing, VB
Computer Science Canada 
Programming C, C++, Java, PHP, Ruby, Turing, VB  

Username:   Password: 
 RegisterRegister   
 Tip: Iterating with an index
Index -> Programming, Ruby -> Ruby Tutorials
View previous topic Printable versionDownload TopicRate TopicSubscribe to this topicPrivate MessagesRefresh page View next topic
Author Message
wtd




PostPosted: Tue Jul 12, 2005 7:42 pm   Post subject: Tip: Iterating with an index

Let's say we want to iterate over an array and print out each element. That's pretty easy.

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

code:
1. hello
2. world
3. foo
4. bar


You might be tempted to write something like:

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

code:
foo = ["hello", "world", "foo", "bar"]

foo.each_with_index do |item, index|
   puts "#{index + 1}. #{item}"
end
Sponsor
Sponsor
Sponsor
sponsor
Cervantes




PostPosted: Wed Jul 13, 2005 7:00 am   Post subject: (No subject)

Nice tip, wtd! Very Happy
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




PostPosted: Wed Jul 13, 2005 1:19 pm   Post subject: (No subject)

Cervantes wrote:
Nice tip, wtd! Very Happy
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:

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

code:
class Foo
   def each
      for x in ["hello", "world", "foo", "bar"]
         yield x
      end
   end
end


We can then, for instance, simply:

code:
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 Proc class.

So let's write a basic collect method.

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

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

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

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

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

code:
Foo.new.collect


Or:

code:
Foo.new.collect { |x| x.upcase }
Cervantes




PostPosted: Wed Jul 13, 2005 9:46 pm   Post subject: (No subject)

Thanks for the explanation on blocks, wtd. Very Happy

I'm curious about that for loop though:
code:

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

/me loads irb

...

Oh man, that's sweet. Very Happy

I was just checking out the Ruby Library Reference and noticed each_with_index is listed as a "mixin". It also seems to be enumerable. Thinking
wtd




PostPosted: Wed Jul 13, 2005 10:12 pm   Post subject: (No subject)

Cervantes wrote:
I'm curious about that for loop though:
code:

      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.

Cervantes wrote:
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. Smile


Correct.

Cervantes wrote:
I was just checking out the Ruby Library Reference and noticed each_with_index is listed as a "mixin". It also seems to be enumerable. Thinking


First, define each.

code:
class Bar
   def each
      for x in 1 .. 4
         yield x
      end
   end
end


Now you can obviously do something meaningless like:

code:
Bar.new.each { |x| puts x }


Now, mix-in enumerable.

code:
class Bar
   include Enumerable
end


Now, try:

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

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




PostPosted: Thu Jul 14, 2005 7:51 am   Post subject: (No subject)

Thanks again, wtd. Very Happy
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




PostPosted: Thu Jul 14, 2005 4:38 pm   Post subject: (No subject)

Cervantes wrote:
Thanks again, wtd. Very Happy
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. Smile

What I showed you was an approximation.

Of course, you can see how this works by creating your own mix-in.

code:
$ 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




PostPosted: Fri Jul 15, 2005 6:55 am   Post subject: (No subject)

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,
code:

Baz.new.Nonsense.each_with_rand

right?

Thanks once again, wtd!
Sponsor
Sponsor
Sponsor
sponsor
wtd




PostPosted: Fri Jul 15, 2005 2:07 pm   Post subject: (No subject)

Cervantes wrote:
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:

code:
Baz::Nonsense


But you wouldn't be able to access the each_with_rand method that way.

code:
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. Smile
cahill




PostPosted: Sun Sep 04, 2005 8:15 am   Post subject: (No subject)

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
Display posts from previous:   
   Index -> Programming, Ruby -> Ruby Tutorials
View previous topic Tell A FriendPrintable versionDownload TopicRate TopicSubscribe to this topicPrivate MessagesRefresh page View next topic

Page 1 of 1  [ 10 Posts ]
Jump to:   


Style:  
Search: