Tip: Iterating with an index
Author |
Message |
wtd
|
Posted: 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
|
|
|
Cervantes
|
Posted: Wed Jul 13, 2005 7:00 am Post subject: (No subject) |
|
|
Nice tip, wtd!
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
|
Posted: Wed Jul 13, 2005 1:19 pm Post subject: (No subject) |
|
|
Cervantes wrote: Nice tip, wtd!
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:
Or:
code: | Foo.new.collect { |x| x.upcase } |
|
|
|
|
|
|
Cervantes
|
Posted: Wed Jul 13, 2005 9:46 pm Post subject: (No subject) |
|
|
Thanks for the explanation on blocks, wtd.
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.
/me loads irb
...
Oh man, that's sweet.
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. |
|
|
|
|
|
wtd
|
Posted: 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.
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.
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
|
Posted: Thu Jul 14, 2005 7:51 am Post subject: (No subject) |
|
|
Thanks again, wtd.
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
|
Posted: Thu Jul 14, 2005 4:38 pm Post subject: (No subject) |
|
|
Cervantes wrote: Thanks again, wtd.
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.
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
|
Posted: 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
|
|
|
wtd
|
Posted: 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:
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. |
|
|
|
|
|
cahill
|
Posted: 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 |
|
|
|
|
|
|
|