重构 Luhn 算法的 Ruby 代码
Refactor Ruby code for Luhn algorithm
帮我重构实现Luhn algorithm,描述如下:
The formula verifies a number against its included check digit, which
is usually appended to a partial account number to generate the full
account number. This account number must pass the following test:
- From the rightmost digit, which is the check digit, moving left, double the value of every second digit; if the product of this doubling operation is greater than 9 (e.g., 8 × 2 = 16), then sum the digits of the products (e.g., 16: 1 + 6 = 7, 18: 1 + 8 = 9).
- Take the sum of all the digits.
- If the total modulo 10 is equal to 0 (if the total ends in zero) then the number is valid according to the Luhn formula; else it is not valid.
Assume an example of an account number "7992739871" that will have a
check digit added, making it of the form 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
The check digit (x) is obtained by computing the sum of digits then
computing 9 times that value modulo 10 (in equation form, (67 × 9 mod
10)). In algorithm form:
- Compute the sum of the digits (67).
- Multiply by 9 (603).
- The last digit, 3, is the check digit. Thus, 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
显然,我在所有这些方面都是菜鸟。但我感谢任何帮助,包括适当的风格!
如何使用嵌套注入方法
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}}
建议:
- 尝试将您的代码封装在 class 中并提供直观的 public API。在私有方法中隐藏算法的内部细节。
- 在最多5行的class中将规则分解成小方法,谨慎地打破这个规则。关注 Sandi Metz Rules.
- 研究问题并找到与问题相关的域名;用它来命名小方法。
- 注重可读性。记住这句话:"Programs must be written for people to read, and only incidentally for machines to execute." 来自 SICP.
的 Hal Abelson
- 阅读Ruby style guide以改进代码格式;是的,找一个更好的编辑器。
- 遵循这些似乎会使代码更加冗长。但它会提高可读性并有助于维护。此外,如果您甚至在个人项目中也倾向于遵循它,那么这个过程将深深地刻在您的身上,很快就会成为第二天性。
考虑到这些,通过以下尝试解决问题:
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
我会像这样实现该算法:
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
好吧,该代码适用于维基百科中的那些示例:)
这里有一些建议给你:
- 在你的函数中去掉 prints/puts 到标准输入,只是 return
价值。对于这个函数 boolean true/false 很好。
- ruby 社区
使用“?”在 return false/true
的方法名称中
- 别忘了
正确格式化你的代码,但也许你还没有学会如何在 Whosebug 上做它(我还没有:)
- 使用 2 个空格缩进代码
帮我重构实现Luhn algorithm,描述如下:
The formula verifies a number against its included check digit, which is usually appended to a partial account number to generate the full account number. This account number must pass the following test:
- From the rightmost digit, which is the check digit, moving left, double the value of every second digit; if the product of this doubling operation is greater than 9 (e.g., 8 × 2 = 16), then sum the digits of the products (e.g., 16: 1 + 6 = 7, 18: 1 + 8 = 9).
- Take the sum of all the digits.
- If the total modulo 10 is equal to 0 (if the total ends in zero) then the number is valid according to the Luhn formula; else it is not valid.
Assume an example of an account number "7992739871" that will have a check digit added, making it of the form 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
The check digit (x) is obtained by computing the sum of digits then computing 9 times that value modulo 10 (in equation form, (67 × 9 mod 10)). In algorithm form:
- Compute the sum of the digits (67).
- Multiply by 9 (603).
- The last digit, 3, is the check digit. Thus, 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
显然,我在所有这些方面都是菜鸟。但我感谢任何帮助,包括适当的风格!
如何使用嵌套注入方法
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}}
建议:
- 尝试将您的代码封装在 class 中并提供直观的 public API。在私有方法中隐藏算法的内部细节。
- 在最多5行的class中将规则分解成小方法,谨慎地打破这个规则。关注 Sandi Metz Rules.
- 研究问题并找到与问题相关的域名;用它来命名小方法。
- 注重可读性。记住这句话:"Programs must be written for people to read, and only incidentally for machines to execute." 来自 SICP. 的 Hal Abelson
- 阅读Ruby style guide以改进代码格式;是的,找一个更好的编辑器。
- 遵循这些似乎会使代码更加冗长。但它会提高可读性并有助于维护。此外,如果您甚至在个人项目中也倾向于遵循它,那么这个过程将深深地刻在您的身上,很快就会成为第二天性。
考虑到这些,通过以下尝试解决问题:
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
我会像这样实现该算法:
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
好吧,该代码适用于维基百科中的那些示例:)
这里有一些建议给你:
- 在你的函数中去掉 prints/puts 到标准输入,只是 return 价值。对于这个函数 boolean true/false 很好。
- ruby 社区 使用“?”在 return false/true 的方法名称中
- 别忘了 正确格式化你的代码,但也许你还没有学会如何在 Whosebug 上做它(我还没有:)
- 使用 2 个空格缩进代码