views:

220

answers:

5

I'm learning Ruby and RoR at the moment, and I came across this:

<% for post in @posts %>

in the Rails guide. I'd understood that the idiomatic way to do this in Ruby is with:

<% @posts.each do |post| %>

If there is a difference then what is it? And if there isn't a difference then wouldn't it be better for the Rails people to be pushing proper Ruby idioms (rather than this, which looks more pythonic to me)?

Edit: I've just found two conflicting explanations of this: Tutorials Point says they're the same except "a for loop doesn't create a new scope for local variables", whereas CS.Auckland.ac.NZ says for is just syntactic sugar for the equivalent .each.

Edit2: The for ... in in question was for the index.html.erb generated in app/views/posts by script/generate scaffold. I've done a quick check and that now generates the .each syntax. I guess that part of the guide was written at an earlier stage of rails development when the scaffold generated for ... in.

Edit3: I can now confirm that for x in y was used in Rails 2.2.2, but by 2.3.8 it is using y.each do |x|. Just so you know.

A: 

The thing is that the first example (for post in @posts) looks more "procedural", while the second (@posts.each do |post|) looks more "functional". Unfortunately, procedural way to read code is still considered more popular, more clear and less "geeky". That's why, perhaps, many would prefer it to the "native" way of iterating through containers.

If it looks pythonic (I'm not sure if it's true), then it may be considered even better, since it improves accessibility of Rails by developers that come from other platforms.

Personally, I'd like ruby and "functional" ways to be pushed. But the above reasons demonstrate that it can be done either way.

Pavel Shved
I say it looks pythonic because `for ... in` is the standard way to iterate in Python.
Skilldrick
@skill, as well as it's a standard way of iterating in other languages (obsolete Perl syntax, for example). It also is the *natural* (in the sense how humans thing about it) way of iterating through any collection.
Pavel Shved
@Pavel, yes, I see what you mean. I wasn't necessarily saying that they were copying Python, just that it looks more like Python.
Skilldrick
A: 

Some day I was developing the feature of generating PDF reports for the website I was working on.

The plugin I was using had many limitations. Those limitations enforced me to produce the solution which was very imperative and procedural. I was using constructions like while or for just to emphasis this style of programming.

In fact, this was the only situation when I used those keywords in ruby.

skalee
+17  A: 

The tutorialpoint page is correct, for is equivalent to each except for the scoping difference. Here's a demonstration:

arr = [1,2,3]
arr.each do |x|
  last = x
end
last # NameError

vs.

arr = [1,2,3]
for x in arr
  last = x
end
last #=> 3

If you want to make it work using each, you need to do last = nil before the loop. This is, as the link pointed out, because blocks start a new scope while for does not.

Note however that this rarely makes a practical difference and few people are even aware of it.

When people use for in ruby it's most often because that's what they're used to coming from other languages - not because of any differences between for and each.

sepp2k
Thanks! Exactly what I was looking for. This takes me back to my original point though, which is why would the Rails team (or at least whoever wrote the Getting Started guide) go with the `for ... in` syntax?
Skilldrick
There's a lot of people who learn rails without having learned ruby first. I suppose the guide is written in a style that's intended to look familiar to people coming from other languages.
sepp2k
Chuck
Performance wise I would imagine `for x in arr` is a tad faster, not that it matters that much
Sam Saffron
There are differences between those constructions, but I doubt if it was real reason for introducing both of them.
skalee
A: 

You may want to use for x in y if you're trying to do ruby profiling and your version of ruby-prof doesn't have method elimination.

Andrew Grimm
A: 

One more thing about for x in y is that you can do for i in 1..5 which will work like a standard for loop, starting at i = 1 and going through i = 5.

The handy thing about this is that you can use a range like 3..8 or x..y, you are not stuck starting from zero, which you would be by using n.times method.

I know this isn't directly related to your example, but it is a useful bit to know I feel.

phoffer
Wouldn't 1.upto(5).each {|x| #something} take care of that?
Jeba Singh Emmanuel
@jeba ya it would. I'm relatively new to ruby, so I hadn't seen that before. So thank you! I learn something new every day!
phoffer
You can also do `(1..5).each`
Skilldrick