用更灵活的解决方案替换if语句

时间:2013-01-23 03:21:10

标签: ruby

我的产品类有一个代码作为属性。代码始终是一个数字,根据产品的第一个数字是某种类型。

If first digit in 0,1,2,3 product is a 'A type'
If first digit in 4,5,6 product is a 'B type'
If first digit in 7,8,9 product is a 'C type'

我正在寻找一种在不使用3分支if声明的情况下确定产品类型的方法。

有什么想法吗?

5 个答案:

答案 0 :(得分:3)

面向对象的方法。显然,match?方法可以用最符合您要求的方式编写,也可以根据您的要求进行调整 - 我在这里使用正则表达式来帮助证明这种方法的灵活性。 TYPES常数& type_for方法应该封装在某个地方,但这取决于你决定在哪里。

class Type
  def initialize name, pattern
    @name = name
    @pattern = pattern
  end

  def match? code
    code =~ @pattern
  end
end

a_type = Type.new 'A', /^[0-3]/
b_type = Type.new 'B', /^[4-6]/
c_type = Type.new 'C', /^[7-9]/
TYPES = [a_type, b_type, c_type]

def type_for product
  TYPES.detect { |type| type.match? product.code }
end

答案 1 :(得分:2)

您是否考虑过使用case声明?我不确定这是否满足你想要摆脱“3分支if语句”的愿望,但也许它会给你一些别的东西来考虑。

case code[0].to_i
when 0..3 then 'A type'
when 4..6 then 'B type'
when 7..9 then 'C type'
end

我假设上面的“code”属性实际上存储为字符串,因为你指定第一个数字可以是0,而在Ruby中,如果它是一个字面整数,它会将你的数字转换为八进制。如果这是一个错误的假设,只需改变代码[0]位。

这有帮助吗?

答案 2 :(得分:1)

我倾向于在我自己的代码中使用这样的东西,因为我宁愿定义一个表格来显示与匹配值和预期输出的关系:

HASH = {
  /\A[0-3]/ => 'A type',
  /\A[4-6]/ => 'B type',
  /\A[7-9]/ => 'C type'
}

def get_type(s)
  HASH.keys.each { |regex| 
    return HASH[regex] if s[regex]
  }
end

[ '0001', '3000', '4000', '9000' ].each do |v|
  puts "#{ v } => #{ get_type(v) }"
end

哪个输出:

0001 => A type
3000 => A type
4000 => B type
9000 => C type

我尝试将类似哈希的内容保存在YAML文件中,这样我们就不必修改代码来添加其他测试/类型。可以使用YAML::load_file()从类似于以下内容的文件轻松初始化HASH常量:

---
? !ruby/regexp /\A[4-6]/
: B type

? !ruby/regexp /\A[0-3]/
: A type

? !ruby/regexp /\A[7-9]/
: C type

并使用简单的puts HASH.to_yaml创建。

尽管如此,我也非常赞成case声明方法。

答案 3 :(得分:0)

def func(code)
  return 'A type' if code[0] == '0'
  ['A','B','C'][(code[0].to_i-1)/3] << ' type'
end

func('0ekrnn')   # => 'A type'
func('4mgm')     # => 'B type'

答案 4 :(得分:0)

您可以使用三元运算符

first_char = code[0].to_i
product_type = [0,1,2,3].include?(first_char) ? "A" : [4,5,6].include?(first_char) ? "B" : [7,8,9].include?(first_char) ? "C" : ""