next versus if in a .each loop?
Solution 1
Totally agree with @DigitalRoss and I have seen people using next
if there is complicated piece of code after some condition is being evaluated i.e.
next if @state!=:some_state
# some long complicated code
on the other hand if there is a simple operation that needs to be performed on the basis of some condition then I would prefer
if @state == :some_state
#call_a_method_to_do_something
end
OR
call_a_method if @state == :some_state
Having said that, its bad practice to write long complicated code. If your code is clean and well designed then you would never have to use next
within your code.
Solution 2
I think that the example you gave doesn't really capture situation where doing next
makes a difference. Consider the situation where you have multiple "next-points" in your code:
text.each do |c|
next if @state == :state1
...
next if @state == :state2
...
next if @state == :state3
...
end
and compare it with if-variant:
text.each do |c|
unless @state == :state1
...
unless @state == :state2
...
unless @state == :state3
...
end
end
end
end
Although the first could be viewed as spaghetti-style by some purists, IMHO it is more readable than the latter.
Solution 3
Do it the second way
Some schools of thought are opposed to things in various languages like next
, retry
, continue
, and break
, because they are just a little bit too much in the direction of the disrespected goto
statement.
Those statements do have their use cases, but in general it's a bad idea to deliberately "spaghettify" the code when a structured downward-pointing construct will accomplish the same thing. Now if the condition called for skipping the entire loop body, in that case I might prefer the use of next
.
Solution 4
i would go for:
text.each{ |c|
...
... Generic state processing
...
case @state
when :state_1 then code
when :state_2 then code
end
}
But if it's as on the first sample ( meaning just 1 state needs extra processing )
text.each{ |c|
...
... Generic state processing
...
next unless @state == :state_1
...
... Code that process states other than :state_1
}
Going even further instead of hitting the instance variable, it sound nicer to ask the object if it's in the state we need like:
def processed?
@state == :state_1
end
...
next unless processed? # sounds like natural language...
...
Reasoning a little bit further, I think one liners like 'next unless processed?' are good only when there are no more than 10 lines of code in the same indentation level, otherwise i rather do the other, so indentation will help me understand at first glance what's happening
Related videos on Youtube
Earlz
Hello there! My name's Jordan Earls, but most people online know me as "earlz". I'm the lead developer and a co-founder of the Qtum project which brings the Ethereum Virtual Machine (ie, the thing that makes Solidity contracts function) to a UTXO based blockchain similar to Bitcoin. I've been programming since I was 13 and am completely self-taught. Low-level code like assembly and pointer arithmetic is the fun stuff for me. I also make music when I have time even though it's usually awful. Most of my personal projects are open source and BSD licensed. The majority of them are at bitbucket with the rest of them being listed on github Also, you can follow me on the twitters @earlzdotnet
Updated on July 21, 2020Comments
-
Earlz almost 4 years
I have a text processing thing I'm doing in Ruby. Basically, I have to implement a simple state machine (with one character look-behind).
My code at the moment looks like this:
text.each{ |c| ... ... ... ... if @state!=:some_state next end #processing stuff for if in :some_state mode ... ... ... ... ... }
Is this proper? Or should it rather be implemented like:
text.each{ |c| ... ... ... ... if @state==:some_state #processing stuff for if in :some_state mode ... ... ... ... ... end }
Is there a right way or is it just preference? Which one blends more with "the ruby way" of doing things?
-
Earlz over 14 yearsWell it's a state machine with a lot of states, so I can only abstract so much away(and it is as DRY as I can make it), so I have no choice but to have over 50 lines of code after the
next
statement in question, so would that qualify as fair use? -
klochner over 14 yearsit's a judgement call - IMHO guard clauses are ok, but they belong at the beginning of a code block.
-
nas over 14 yearsIf you can get your code working easily with an
if
statement then still avoidnext
. However, if you have a 50 line of code within a single method then I think you should still give it a hard look as that code might be doing too many things. Try to abstract bits of it in separate methods. Methods or functions should be small, have same level of abstraction with single responsibility and either change the state of the object or return a value. -
Wayne Conrad over 14 years"Don't use goto (or anything like it)" is a rule devoid of context; rigid adherence to it as dogma makes impossible some constructs that cause the code to be clearer. The context is, "because it can make code difficult to follow and to refactor." Since the appropriate use of "next", "break" exceptions, & etc. can make code easier to read and refactor, they may be used even though they are forms of "goto."
-
rampion over 14 yearsI normally reserve my
next if predicate
use to the start of a block, just for clarity. -
DigitalRoss about 12 yearsSure, I didn't mean to give a doctrinaire impression. Sometimes next/break/continue is exactly the right solution. But the OP asked which was better and I'm pretty sure I gave the right answer.
-
Wayne Conrad about 12 yearsI think you're right. I'm not sure why I went off with the nano lecture back in Frebruary -- you didn't say anything that ought to have deserved it.
-
Kabir Sarin about 10 yearsGotos and the like are (almost) always bad. I remember reading this paper back when I was in school: files.ifi.uzh.ch/rerg/arvo/courses/kvse/uebungen/…