【问题标题】:How should I refactor this conditional code in ruby?我应该如何在 ruby​​ 中重构这个条件代码?
【发布时间】:2016-12-18 22:09:04
【问题描述】:

如何重构这个函数?

  def split_description(first_n)
    description_lines = description.split "\n"
    line_num = description_lines.length
    if line_num > first_n
      @description_first = to_html(description_lines[0..first_n].join("\n"))
      @description_remain = to_html(description_lines[first_n + 1..line_num].join("\n"))
    elsif line_num > 1
      @description_first = to_html(description_lines[0..first_n].join("\n"))
      @description_remain = ''
    else
      @description_first = ''
      @description_remain = ''
    end
  end

我是 Ruby 初学者,遇到这个 rubocup 警告:Method has too many lines. [13/10]

以下是完整的代码网址: https://github.com/RubyStarts3/YPBT-app/blob/master/views_objects/video_info_view.rb

【问题讨论】:

    标签: ruby refactoring


    【解决方案1】:

    代码

    def split_description(description, first_n)
      @description_first, @description_remain =
      case description.count("\n")
      when 0..first_n
        [description, '']
      else
        partition_description(description, first_n)
      end.map(&:to_html)
    end
    
    def partition_description(description, first_n)
      return ['', description] if first_n.zero?
      offset = 0
      description.each_line.with_index(1) do |s,i|
        offset += s.size
        return [description[0,offset], description[offset..-1]] if i == first_n
      end
    end
    

    我假设to_html('') #=> '',但如果不是这样,修改很简单。

    示例

    为了看到to_html的效果,我们就这么定义吧。

    def to_html(description)
      description.upcase
    end
    
    description =<<_
    It was the best of times
    it was the worst of times
    it was the age of wisdom
    it was the age of fools
    _
    

    split_description(description, 0)
    @description_first
      #=> "" 
    @description_remain
      #=> "IT WAS THE BEST OF TIMES\n..WORST OF TIMES\n..AGE OF WISDOM\n..AGE OF FOOLS\n" 
    
    split_description(description, 1)
    @description_first
      #=> "IT WAS THE BEST OF TIMES\n" 
    @description_remain
      #=> "IT WAS THE WORST OF TIMES\n..AGE OF WISDOM\n..AGE OF FOOLS\n" 
    
    split_description(description, 2)
    @description_first
      #=> "IT WAS THE BEST OF TIMES\nIT WAS THE WORST OF TIMES\n" 
    @description_remain
      #=> "IT WAS THE AGE OF WISDOM\nIT WAS THE AGE OF FOOLS\n" 
    
    split_description(description, 3)
    @description_first
      #=> "IT WAS THE BEST OF TIMES\n..WORST OF TIMES\n..AGE OF WISDOM\n" 
    @description_remain
      #=> "IT WAS THE AGE OF FOOLS\n" 
    
    split_description(description, 4)
    @description_first
      #=> "IT WAS THE BEST OF TIMES\n..WORST OF TIMES\n..AGE OF WISDOM\n..AGE OF FOOLS\n" 
    @description_remain
      #=> "" 
    

    说明

    首先,description 似乎是一个保存字符串的局部变量。如果是这样,它必须是方法的参数(连同first_n)。

    def split_description(description, first_n)
    

    我们想给两个实例变量赋值,所以让我们开始写

      @description_first, @description_remain =
    

    实际上有两个步骤:获取所需的字符串,然后将它们映射到to_html。所以我们先把注意力集中在第一步。

    我们现在以字符串中的行数为条件

      case description.count("\n")
    

    首先,我们来处理字符串不包含换行符的情况

      when 0
        [description, '']
    

    如果字符串为空,则为['', ''];否则它将包含一个没有换行符的字符串。

    接下来,假设字符串中的换行数介于 1 和 first_n 之间。在这种情况下,@description_first 是整个字符串,@description_remain 是空的。

      when 1..first_n
          [description, '']
    

    由于when 0when 1..first_n 返回相同的二元数组,我们可以将它们组合起来:

      when 0..first_n
        [description, '']
    

    到目前为止,first_n 小于换行符的数量。对于换行数大于first_n的情况,我使用了另一种方法。

      else
        partition_description(description, first_n)
    

    partition_description 只是确定first_nth 换行符到description 的偏移量,然后相应地对字符串进行分区。

    最后,我们需要结束case语句,映射to_html返回的两个字符串数组,结束方法

      end.map(&:to_html)
    end
    

    正如我之前提到的,我假设to_html('') #=&gt; ''。在我看来,这似乎是处理空字符串的最佳场所。

    请注意,我直接处理了字符串,而不是将字符串拆分为行,操作这些行然后重新加入它们。

    【讨论】:

      【解决方案2】:

      由于它在每个条件下都被使用或置空,因此将实例变量初始化为空白。

        def split_description(first_n)
          description_lines = description.split "\n"
          line_num = description_lines.length
      
          @description_first = ''
          @description_remain = ''
      
          if line_num > first_n
            @description_first = to_html(description_lines[0..first_n].join("\n"))
            @description_remain = to_html(description_lines[first_n + 1..line_num].join("\n"))
          elsif line_num > 1
            @description_first = to_html(description_lines[0..first_n].join("\n"))
          end
        end
      

      我还将description_lines[first_n + 1..line_num].join("\n") 的逻辑移动到to_html( whatever_that_is( lines, from, to) ) 之类的方法中。那么如果你重复同样的调用并不会那么糟糕,并且名称将描述它在做什么。

      【讨论】:

        【解决方案3】:

        如果first_n 始终大于 1,我认为您可以稍微修改一下 Schwern 的答案:

        ...
        
        @description_first = to_html(description_lines[0..first_n].join("\n")) if line_num > 1
        
          if line_num > first_n
                @description_remain = to_html(description_lines[first_n + 1..line_num].join("\n"))
          end
        end
        

        【讨论】:

          【解决方案4】:

          这应该可行:

          def split_description(description, first_n = 0)
            lines = description.each_line
            @description_first  = to_html(lines.take(first_n).join)
            @description_remain = to_html(lines.drop(first_n).join)
          end
          

          takedrop 替换您的所有逻辑,因为正如@Cary Swoveland 在评论中提到的那样:

          • 如果你拿太多,你会得到完整的数组,没有错误消息
          • 如果你丢弃太多,你会以一个空数组结束 ud,没有错误消息

          例子:

          [1,2].take(99) #=> [1, 2]
          [1,2].drop(99) #=> []
          

          同样each_line 输出一个字符串数组,换行符仍然存在。不需要splitchompjoin("\n")

          【讨论】:

          • 确实如此!不错,埃里克。当与我的答案中的示例一起使用时,我们会得到相同的结果。您正在利用 [1,2].take(99) #=&gt; [1, 2][1,2].drop(99) #=&gt; [] 的事实,这可能使读者难以理解正在发生的事情。我不建议你改变它,但也许添加一两个 cmets 来澄清。我在说什么呢?发布到 SO 的方法中的评论?
          猜你喜欢
          • 2011-03-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-04-18
          • 1970-01-01
          相关资源
          最近更新 更多