13

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.

Skilldrick
  • 69,215
  • 34
  • 177
  • 229

5 Answers5

21

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
  • 363,768
  • 54
  • 674
  • 675
  • 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 Jul 27 '10 at 11:35
  • 2
    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 Jul 27 '10 at 11:41
  • A related difference between the two that isn't obvious just from that example: `a=[]; for x in [1,2,3,4]; a << proc {x} end; map(&:call)` gives `[4,4,4,4]` while the equivalent with `each` would give `[1,2,3,4]` because `each` introduces a new `x` binding with each iteration. – Chuck Jul 28 '10 at 00:30
  • Performance wise I would imagine `for x in arr` is a tad faster, not that it matters that much – Sam Saffron Aug 04 '10 at 04:03
  • There are differences between those constructions, but I doubt if it was real reason for introducing both of them. – skalee Aug 07 '10 at 10:57
0

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.

P Shved
  • 96,026
  • 17
  • 121
  • 165
  • I say it looks pythonic because `for ... in` is the standard way to iterate in Python. – Skilldrick Jul 27 '10 at 10:57
  • 1
    @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. – P Shved Jul 27 '10 at 11:04
  • yes, I see what you mean. I wasn't necessarily saying that they were copying Python, just that it looks more like Python. – Skilldrick Jul 27 '10 at 11:11
0

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
  • 12,331
  • 6
  • 55
  • 57
0

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.

Community
  • 1
  • 1
Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
0

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.

Paul Hoffer
  • 12,606
  • 6
  • 28
  • 37