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

时间:2015-04-15 00:52:49

标签: ruby if-statement case dry

我常常发现自己的if / else语句与不同的值相比,基本上是重复的同一行。在这里,我编写代码来预测致命流感爆发造成的死亡人数。它已被重构为一个案例陈述,仍然非常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

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

如何让重复的个案陈述更干?

修改

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

为了便于阅读:

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

5 个答案:

答案 0 :(得分:1)

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

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,但在方法的参数中包含了用于传入值的局部变量。你不能这样做。删除@以将变量转换为局部变量。

答案 1 :(得分:1)

一些事情:

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

答案 2 :(得分: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

答案 3 :(得分:0)

    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

答案 4 :(得分:0)

你可以用油煮沸,但我使用哈希而不是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
相关问题