这种校验位方法可以重构吗?

时间:2011-08-29 15:33:17

标签: ruby refactoring

我有一个跟踪号码上的校验位的方法,但它只是感觉冗长/草率。它可以被重构并且通常被清理干净吗?

我正在运行Ruby 1.8.7。

def is_fedex(number)
  n = number.reverse[0..14]

  check_digit = n.first.to_i

  even_numbers = n[1..1].to_i + n[3..3].to_i + n[5..5].to_i + n[7..7].to_i + n[9..9].to_i + n[11..11].to_i + n[13..13].to_i

  even_numbers = even_numbers * 3

  odd_numbers = n[2..2].to_i + n[4..4].to_i + n[6..6].to_i + n[8..8].to_i + n[10..10].to_i + n[12..12].to_i + n[14..14].to_i

  total = even_numbers + odd_numbers

  multiple_of_ten = total + 10 - (total % 10)

  remainder = multiple_of_ten - total

  if remainder == check_digit
    true
  else
    false
  end
end

编辑:以下是有效和无效的数字。

有效身份:9612019950078574025848

无效:9612019950078574025847

6 个答案:

答案 0 :(得分:2)

def is_fedex(number)
  total = (7..20).inject(0) {|sum, i| sum + number[i..i].to_i * ( i.odd? ? 1 : 3 ) }
  number[-1].to_i == (total / 10.0).ceil * 10 - total
end

我相信你应该保留你的代码。虽然它不是惯用的或聪明的,但是从现在开始几个月你就会很难理解它。

答案 1 :(得分:1)

我不是一个红宝石程序员,所以如果任何语法都没有,我道歉但你应该得到一般的想法。我看到的一些事情:首先,您不需要对数组进行切片,单个索引就足够了。第二,你可以做类似的事情,而不是分裂甚至是奇数:

total = 0
for i in (1..14)
  total += n[i].to_i * ( i % 2 == 1 ? 1 : 3 )
end

第三,余数可以简化为10 - (总%10)。

答案 2 :(得分:1)

我意识到你正在运行1.8.7,但这是我尝试使用each_slice并同时注入一个1.9.2功能:

def is_fedex(number)
  total = number.reverse[1..14].split(//).map(&:to_i).each_slice(2).inject(0) do |t, (e,o)|
    t += e*3 + o 
  end
  10 - (total % 10) == number[-1].to_i 
end

它通过了两个测试

答案 3 :(得分:0)

尝试一下:

#assuming number comes in as a string
def is_fedex(number)
  n = number.reverse[0..14].scan(/./)
  check_digit = n[0].to_i
  total = 0
  n[1..14].each_with_index {|d,i| total += d.to_i * (i.even? ? 3 : 1) }
  check_digit == 10 - (total % 10)
end

> is_fedex("12345678901231")  => true

编辑合并了简化的余数逻辑,如Kevin建议的那样

答案 4 :(得分:0)

这样的东西?

def is_fedex(number)
  even_arr, odd_arr = number.to_s[1..13].split(//).map(&:to_i).partition.with_index { |n, i| i.even? }
  total = even_arr.inject(:+) * 3 + odd_arr.inject(:+)

  number.to_s.reverse[0..0].to_i == (total + 10 - (total % 10)) - total
end

如果你能给我一个有效且无效的号码,我可以测试它是否有效,并可能进一步调整它:)

答案 5 :(得分:0)

此功能应该:

def is_fedex(number)
  # sanity check
  return false unless number.length == 15

  data = number[0..13].reverse
  check_digit = number[14..14].to_i

  total = (0..data.length-1).inject(0) do |total, i|
    total += data[i..i].to_i * 3**((i+1)%2)
  end

  (10 - total % 10) == check_digit
end

算术表达式3**((i+1)%2)可能看起来有点复杂,但与(i.odd? ? 1 : 3)基本相同。两种变体都是正确的,你使用的是你自己(可能会影响速度......)

另请注意,如果您使用Ruby 1.9,则可以使用data[i]而不是{1.8}所需的data[i..i]