next versus if in a .each loop?

25,162

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

Share:
25,162

Related videos on Youtube

Earlz
Author by

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, 2020

Comments

  • Earlz
    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
    Earlz over 14 years
    Well 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
    klochner over 14 years
    it's a judgement call - IMHO guard clauses are ok, but they belong at the beginning of a code block.
  • nas
    nas over 14 years
    If you can get your code working easily with an if statement then still avoid next. 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
    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
    rampion over 14 years
    I normally reserve my next if predicate use to the start of a block, just for clarity.
  • DigitalRoss
    DigitalRoss about 12 years
    Sure, 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
    Wayne Conrad about 12 years
    I 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
    Kabir Sarin about 10 years
    Gotos 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/…