代码之家  ›  专栏  ›  技术社区  ›  sarkon

Luhn算法的重构Ruby代码

  •  0
  • sarkon  · 技术社区  · 10 年前

    帮助我重构实现 Luhn algorithm ,描述如下:

    公式根据包含的校验位验证数字 通常附加到部分帐号以生成完整的 账号。此帐号必须通过以下测试:

    1. 从最右边的数字(即校验数字)向左移动,每第二个数字的值加倍;如果该加倍运算的乘积大于9(例如,8 2=16),则对乘积的位数求和(例如,16∶1+6=7,18∶1+8=9)。
    2. 取所有数字的和。
    3. 如果总模10等于0(如果总模以零结尾),则根据Luhn公式,该数字是有效的;否则无效。

    假设一个帐号“7992739871”的示例 添加校验位,格式为7992739871x:

    • Account number 7 9 9 2 7 3 9 8 7 1 x
    • Double every other 7 18 9 4 7 6 9 16 7 2 -
    • Sum of digits 7 9 9 4 7 6 9 7 7 2 =67

    校验位(x)是通过计算数字的和获得的,然后 计算该值的9倍模10(方程式形式,(679 mod 10)). 算法形式:

    1. 计算数字之和(67)。
    2. 乘以9(603)。
    3. 最后一位数字3是校验位。因此,x=3。

    下面是我的实现,我相信它会更好。

    def credit_check(num)
    verify = num.to_s.split('').map(&:to_i)
    
    half1 = verify.reverse.select.each_with_index { |str, i| i.even? }
    half1 = half1.inject(0) { |r,i| r + i }
    
    # This implements rule 1
    half2 = verify.reverse.select.each_with_index { |str, i| i.odd? }     
    double = half2.map { |n| n * 2 }
    double = double.map { |n| n.to_s.split('') }
    double = double.flatten.map(&:to_i) 
    double = double.inject(0) { |r,i| r + i }
    
    final = double + half1   
    
    puts final % 10 == 0 && (num.to_s.length > 12 && num.to_s.length < 17) ? "VALID" : "INVALID"
    end
    

    很明显,我在这一切上都是个无名小卒。但我感谢任何帮助,包括正确的风格!

    3 回复  |  直到 10 年前
        1
  •  2
  •   Jikku Jose    10 年前

    建议:

    1. 尝试将代码封装在类中,并提供直观的公共API。在私有方法中隐藏算法的内部细节。
    2. 在类中,将规则分成最多5行的小方法,谨慎地打破这个规则。跟随 Sandi Metz Rules .
    3. 研究问题并找到与问题相关的域名;用它来命名小方法。
    4. 注重可读性。记住这句话: “程序必须是为人们阅读而编写的,只是为了让机器执行。” 作者:Hal Abelson,来自 SICP .
    5. 阅读 Ruby style guide 改进代码格式;是的,找一个更好的编辑。
    6. 遵循这些可能会使代码更加冗长。但它将提高可读性并有助于维护。此外,如果你甚至在个人项目中也倾向于遵循它,这个过程将深深地刻进你的心里,并很快成为第二天性。

    考虑到这些,请尝试解决以下问题:

    class CreditCard
      VALID_LENGTH_RANGE = 12..17
    
      def initialize(number)
        @number = number.to_s
      end
    
      def valid?
        valid_length? && check_sum_match?
      end
    
      private
    
      def valid_length?
        VALID_LENGTH_RANGE.include? @number.length
      end
    
      def check_sum_match?
        check_sum.end_with? check_digit
      end
    
      def check_sum
        digits = check_less_number
                 .reverse
                 .each_char
                 .each_with_index
                 .map do |character, index|
          digit = character.to_i
          index.even? ? double_and_sum(digit) : digit
        end
    
        digits.reduce(:+).to_s
      end
    
      def check_less_number
        @number[0..-2]
      end
    
      def check_digit
        @number[-1]
      end
    
      def double_and_sum(digit)
        double = digit * 2
        tens = double / 10
        units = double % 10
    
        tens + units
      end
    end
    

    因此,您可以按如下方式使用它:

    CreditCard.new(222222222224).valid? # => true
    CreditCard.new(222222222222).valid? # => false
    
        2
  •  0
  •   pranav prashant    10 年前

    如何使用嵌套注入方法

     half2  = verify.reverse.select.each_with_index { |str, i| i.odd? }
     double = half2.map { |n| n * 2 }
    
     double = double.inject(0){|x,y| x + y.to_s.split("").inject(0){|sum, n| sum + n.to_i}}
    
        3
  •  0
  •   Anton Harniakou    10 年前

    我会这样实现算法:

    def credit_card_valid?(num)
      digits = String(num).reverse.chars.map(&:to_i)
      digits.each_with_index.reduce(0) do |acc, (value, index)|
        acc + if index.even?
                value
              else
                double_value = value * 2
                if double_value > 9
                  double_value.to_s.split('').map(&:to_i).reduce(&:+)
                else
                  double_value
                end
              end
      end % 10 == 0
    end
    

    这段代码适用于维基百科中的示例:)

    以下是一些建议:

    • 在函数中消除stdin的打印/输出,只需返回 价值对于这个函数,布尔值true/false是好的。
    • 红宝石群落 使用“?”在返回false/true的方法名中
    • 别忘了 正确格式化代码,但可能你还没有学会如何在Stacksoverflow上进行格式化(我还没有:)
    • 使用2个空格缩进代码