【问题标题】:next versus if in a .each loop?next 与 if 在 .each 循环中?
【发布时间】:2010-02-17 04:22:40
【问题描述】:

我正在用 Ruby 进行文本处理。基本上,我必须实现一个简单的状态机(一个字符后视)。

我现在的代码是这样的:

text.each{ |c|
  ...
  ...
  ...
  ...
  if @state!=:some_state
    next
  end
  #processing stuff for if in :some_state mode
  ...
  ...
  ...
  ...
  ...
}

这样合适吗?还是应该像这样实现:

text.each{ |c|
  ...
  ...
  ...
  ...
  if @state==:some_state
    #processing stuff for if in :some_state mode
    ...
    ...
    ...
    ...
    ...
  end
}

有正确的方法还是只是偏好?哪一种更能融入“红宝石之道”的做事方式?

【问题讨论】:

    标签: ruby coding-style


    【解决方案1】:

    完全同意@DigitalRoss,我见过有人使用next,如果在评估某些条件后有一段复杂的代码,即

     next if @state!=:some_state
     # some long complicated code
    

    另一方面,如果有一个简单的操作需要在某些条件的基础上执行,那么我更喜欢

     if @state == :some_state
       #call_a_method_to_do_something
     end
    
     OR
    
     call_a_method if @state == :some_state
    

    话虽如此,编写冗长复杂的代码是不好的做法。如果您的代码干净且设计良好,那么您将永远不必在代码中使用next

    【讨论】:

    • 嗯,它是一个有很多状态的状态机,所以我只能抽象出这么多(而且它是尽可能干的),所以我别无选择,只能有超过 50 个有问题的 next 语句之后的代码行,那么这是否符合合理使用的条件?
    • 这是一个判断调用 - 恕我直言,保护条款是可以的,但它们属于代码块的开头。
    • 如果您可以使用if 语句让您的代码轻松运行,那么仍然避免使用next。但是,如果您在一个方法中有 50 行代码,那么我认为您仍然应该仔细研究一下,因为该代码可能会做太多事情。尝试在单独的方法中抽象它的一部分。方法或函数应该很小,具有相同的抽象级别和单一职责,并且可以更改对象的状态或返回一个值。
    • 为了清楚起见,我通常将我的 next if predicate 使用保留在块的开头。
    【解决方案2】:

    我认为你给出的例子并没有真正捕捉到next 产生影响的情况。考虑一下您的代码中有多个“下一个点”的情况:

    text.each do |c|
      next if @state == :state1
      ...
      next if @state == :state2
      ...
      next if @state == :state3
      ...
    end
    

    并将其与 if-variant 进行比较:

    text.each do |c|
      unless @state == :state1
        ...
        unless @state == :state2
          ...
          unless @state == :state3
            ...
          end
        end
      end
    end
    

    虽然第一个可能被一些纯粹主义者视为意大利面风格,但恕我直言,它比后者更具可读性。

    【讨论】:

      【解决方案3】:

      第二种方法

      有些学派反对nextretrycontinuebreak等各种语言的东西,因为它们只是有点过于偏向不尊重的goto声明。

      这些语句确实有它们的用例,但一般来说,当结构化的向下构造将完成相同的事情时,故意将代码“意大利面条化”是一个坏主意。现在,如果条件要求跳过整个循环体,在这种情况下,我可能更喜欢使用next

      【讨论】:

      • “不要使用 goto(或类似的东西)”是一个没有上下文的规则;严格遵守它作为教条使某些使代码更清晰的结构变得不可能。上下文是,“因为它会使代码难以遵循和重构。”由于适当使用“next”、“break”异常等可以使代码更易于阅读和重构,即使它们是“goto”的形式,也可以使用它们。
      • 当然,我并不是要给人一种教条主义的印象。有时 next/break/continue 正是正确的解决方案。但是 OP 询问哪个更好,我很确定我给出了正确的答案。
      • 我认为你是对的。我不知道为什么我要在 2 月份参加 nano 讲座——你没有说任何应得的。
      • Gotos 之类的(几乎)总是不好的。我记得我在学校的时候读过这篇论文:files.ifi.uzh.ch/rerg/arvo/courses/kvse/uebungen/…
      【解决方案4】:

      我会去:

      text.each{ |c|
        ...
        ... Generic state processing
        ...
        case @state
          when :state_1 then code
          when :state_2 then code
        end
      }
      

      但如果它与第一个样本一样(意味着只有一个状态需要额外处理)

      text.each{ |c|
        ...
        ... Generic state processing
        ...
        next unless @state == :state_1
        ...
        ... Code that process states other than :state_1
      }
      

      更进一步,而不是点击实例变量,询问对象是否处于我们需要的状态听起来更好,例如:

      def processed?
        @state == :state_1
      end
      
      ...
      next unless processed? # sounds like natural language...
      ...
      

      再进一步推理,我认为一个衬里会像“下一个,除非经过处理?”仅当同一缩进级别中的代码不超过 10 行时才有效,否则我宁愿使用其他代码,因此缩进将帮助我第一眼了解正在发生的事情

      【讨论】:

        猜你喜欢
        • 2012-11-13
        • 2019-11-03
        • 1970-01-01
        • 2018-09-29
        • 2019-12-12
        • 2019-01-23
        • 1970-01-01
        • 2015-12-18
        • 2015-11-12
        相关资源
        最近更新 更多