使重复的 if/else 或 case 语句更干

Making repetitive if/else or case statements more DRY

我经常发现自己的 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 语句更干?

编辑

这里的两个建议似乎是两个非常不同但同样好的重构:

为了便于阅读:

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

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

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

我经历了几个步骤来重构这样的代码,首先是寻找重复的部分并尝试将它们移动到条件测试的 down/out(本中的 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,但方法参数中有局部变量,这些变量用于传递值。你不能那样做。删除 @ 将变量变成局部变量。

    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

几件事:

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

然后您可以将代码段重写为:

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) 如果传入相同的变量,请不要使用实例变量!很混乱。

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

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

你可以用油煮我,但我会使用散列而不是 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