【问题标题】:Making repetitive if/else or case statements more DRY使重复的 if/else 或 case 语句更加 DRY
【发布时间】:2015-04-15 00:52:49
【问题描述】:

我经常发现自己的 if/else 语句在与不同的值进行比较时基本上是重复的同一行。在这里,我正在编写代码来预测致命流感爆发造成的死亡人数。它已被重构为一个 case 语句,仍然很 WET:

  def predicted_deaths(population_density, population, state)
    case
      when population_density >= 200 then number_of_deaths = (population * 0.4).floor
      when population_density >= 150 then number_of_deaths = (population * 0.3).floor
      when population_density >= 100 then number_of_deaths = (population * 0.2).floor
      when population_density >= 50 then number_of_deaths = (population * 0.1).floor
      else number_of_deaths = (population * 0.05).floor
    end
    ap "#{state} will lose #{number_of_deaths} people in this outbreak"
  end

我尝试了一些可以使用的东西

j = 0.4
i = 200
  until i <= 50 do
    @population_density >= i then number_of_deaths = (@population * j).floor
    i -= 50
    j -= 0.1
  end

但这并没有真正做同样的事情。

如何使重复的 case 语句更 DRY?

编辑

这里的两个建议看起来像是两个非常不同但同样好的重构:

为了便于阅读:

def predicted_deaths(population_density, population, state)
  factor = case population_density
    when 0...50 then 0.05
    when 50...100 then 0.1
    when 100...150 then 0.2
    when 150...200 then 0.3
    else 0.4
   end
  number_of_deaths = (population * factor).floor
  ap "#{state} will lose #{number_of_deaths} people in this outbreak"
end

更简洁但可读性较差:

def predicted_deaths(population_density, population, state)
  number_of_deaths = (population * 0.05).floor
  for i in 1..4
    number_of_deaths = (0.1 * i * population).floor if population_density.between?(50*i, 50*(i+1)) || population_density >= 200
  end
  ap "#{state} will lose #{number_of_deaths} people in this outbreak"
end

【问题讨论】:

  • 这是一件小事,但我不认为在方法内打印结果是好的编程习惯。最好删除state 参数并返回死亡人数,然后将其打印在方法之外。这样,如果您以后在代码中的其他地方需要死亡人数,您就不必更改方法。

标签: ruby if-statement case dry


【解决方案1】:

一种方法是缩短你的案例陈述:

def predicted_deaths(population_density, population, state)
  factor = case population_density
    when 0...50 then 0.05
    when 50...100 then 0.1
    when 100...150 then 0.2
    when 150...200 then 0.3
    else 0.4
   end
  number_of_deaths = (population * factor).floor
  ap "#{state} will lose #{number_of_deaths} people in this outbreak"
end

【讨论】:

  • 这将是我的第一个重构步骤。
  • 我相信范围查询略有偏差。例如,如果人口密度为 50、100、150、200,则因子将分别为 0.05、0.1、0.2、0.3。例如:(150..200).include?(200)true(150...200).include?(200)false
  • @KevinSylvestre 你是对的,我需要非包含范围。固定。
  • @MarkThomas 我想是的。它会因负人口密度而失败(可能不可能?)并且也会因浮动人口密度而失败(没关系 - 认为它与边缘不匹配,但那是因为我测试了 .. 并更改了索引)。同样,虽然我喜欢这段代码的外观,但在某种意义上它并不是很健壮。
  • (0...50).include? 49.5 #=> 真
【解决方案2】:

考虑一下这个未经测试的代码:

def predicted_deaths(population_density, population, state)
  pct = case
        when population_density >= 200 
          0.4
        when population_density >= 150 
          0.3
        when population_density >= 100 
          0.2
        when population_density >= 50 
          0.1
        else
          0.05
        end

  ap "#{state} will lose #{ (population * pct).floor } people in this outbreak"
end

我经历了几个步骤来重构这样的代码,首先是寻找重复的部分并尝试将它们移出/移出条件测试(case 语句在这个......呃......案例)。

这是第一关:

def predicted_deaths(population_density, population, state)
  number_of_deaths = case
                     when @population_density >= 200 
                       (@population * 0.4).floor
                     when @population_density >= 150 
                       (@population * 0.3).floor
                     when @population_density >= 100 
                       (@population * 0.2).floor
                     when @population_density >= 50 
                       (@population * 0.1).floor
                     else 
                       (@population * 0.05).floor
                     end
  ap "#{@state} will lose #{number_of_deaths} people in this outbreak"
end

在这一点上,@population *floor 显然是多余的,所以我把它们移了下来。

虽然您使用实例变量和局部变量存在问题。您引用了@population_density@population@state,但在方法的参数中有用于传递值的局部变量。你不能那样做。去掉@,把变量变成局部变量。

【讨论】:

  • 是的,关于我对实例变量的引用,对此感到抱歉。我从具有更多方法的更大实例中获取了这段 sn-p 代码,它在上下文中更有意义。为了清楚起见,也许我应该删除帖子中的@?
  • 问题中的代码必须准确反映程序中的代码。否则,它就违背了在这里提出问题的目的,因为我们将没有准确答案的依据。
【解决方案3】:

一些事情:

1) 您可以使 case 语句使用单个赋值:

state = case(city)
when "Miami" then "Florida"
when "Omaha" then "Nebraska"
...
end

2) 您可以创建一个包含不常见逻辑的辅助函数(这将有助于显示什么不是 DRY):

def percentage_of_deaths_for_population_density(population_density)
  case
  when population_density >= 200 then 0.4
  when population_density >= 150 then 0.3
  when population_density >= 100 then 0.2
  when population_density >= 50 then 0.1
  else 0.05
end

然后可以将sn-p改写为:

def predicted_deaths(population_density, population, state)
  number_of_deaths = (population * percentage_of_deaths_for_population_density(population_density)).floor
  ap "#{state} will lose #{number_of_deaths} people in this outbreak"
end

3) 最后,如果您在意,您可以进一步重构 percentage_of_deaths_for_population(但我认为它非常易读,可能会留下它 - 这真的只是在您有一个巨大的声明时):

def percentage_of_deaths_for_population(population)
  { 200 => 0.4, 150 => 0.3, 100 => 0.2, 50 => 0.1 }.each do |limit, ratio|
    return ratio if population >= limit
  end
  return 0.05
end

4) 如果您传递相同的变量,请不要使用实例变量!非常混乱。

【讨论】:

    【解决方案4】:
        def predicted_death(population_density, population, state)
          number_of_death = (@population * 0.05).floor
          for i in 1..4 
            number_of_death = (0.1 * i * @population).floor if @population_density.between?(50*i, 50*(i+1)) || @population_density >= 200
          end
          ap "#{@state} will lose #{number_of_deaths} people in this outbreak"
        end
    

    【讨论】:

      【解决方案5】:

      你可以在油里煮我,但我会使用哈希而不是 case 语句。

      FACTORS_BY_DENSITY = { 200=>0.4, 150=>0.3, 100=>0.2, 50=>0.1, 0=>0.05 }
      
      @data_by_state = {..., "Utah"=>{pop: 4_325_641, pop_density: 126 },...}
      
      def predicted_deaths(state)
        state_data = @data_by_state[state]
        (state_data[:pop] * factor(state_data[:pop_density])).to_i
      end
      
      def factor(density)
        FACTORS_BY_DENSITY.find { |d,_| density >= d }.pop
      end
      
      state = "Utah"
      deaths = predicted_deaths(state)
        #=> 865128 
      puts "#{state} will lose #{deaths} people in this outbreak"
        # Utah will lose 865128 people in this outbreak
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-09-10
        • 1970-01-01
        • 1970-01-01
        • 2021-06-30
        • 1970-01-01
        • 1970-01-01
        • 2022-08-23
        相关资源
        最近更新 更多